From 9cb049276dcf166540f74bfc85c9d8581a163527 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Wed, 20 May 2026 21:44:09 +0200 Subject: [PATCH 01/87] feat: Propagate underlying MPT flags to vault shares (#7077) Signed-off-by: dependabot[bot] Co-authored-by: Denis Angell Co-authored-by: Fomo <508629+shortthefomo@users.noreply.github.com> Co-authored-by: Bart Co-authored-by: Ayaz Salikhov Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- include/xrpl/ledger/View.h | 2 +- include/xrpl/ledger/helpers/MPTokenHelpers.h | 70 +- .../xrpl/ledger/helpers/RippleStateHelpers.h | 4 +- include/xrpl/ledger/helpers/TokenHelpers.h | 33 +- .../xrpl/protocol/detail/ledger_entries.macro | 1 + include/xrpl/protocol/detail/sfields.macro | 1 + .../ledger_entries/MPTokenIssuance.h | 35 + include/xrpl/tx/invariants/MPTInvariant.h | 15 + .../transactors/token/MPTokenIssuanceCreate.h | 23 +- src/libxrpl/ledger/View.cpp | 31 +- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 153 +++- src/libxrpl/ledger/helpers/TokenHelpers.cpp | 28 +- src/libxrpl/tx/invariants/MPTInvariant.cpp | 88 +++ .../lending/LoanBrokerCoverWithdraw.cpp | 10 +- .../token/MPTokenIssuanceCreate.cpp | 22 + .../tx/transactors/vault/VaultCreate.cpp | 26 +- .../tx/transactors/vault/VaultWithdraw.cpp | 9 +- src/test/app/Invariants_test.cpp | 110 ++- src/test/app/Loan_test.cpp | 229 ++++-- src/test/app/MPToken_test.cpp | 19 +- src/test/app/Vault_test.cpp | 680 +++++++++++++++++- .../ledger_entries/MPTokenIssuanceTests.cpp | 27 + 22 files changed, 1483 insertions(+), 133 deletions(-) diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 4958a89d8c..0d76c98a73 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -57,7 +57,7 @@ isVaultPseudoAccountFrozen( ReadView const& view, AccountID const& account, MPTIssue const& mptShare, - int depth); + std::uint8_t depth); [[nodiscard]] bool isLPTokenFrozen( diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h index 6544b18dd1..491bd1933e 100644 --- a/include/xrpl/ledger/helpers/MPTokenHelpers.h +++ b/include/xrpl/ledger/helpers/MPTokenHelpers.h @@ -27,14 +27,18 @@ isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue); isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); [[nodiscard]] bool -isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth = 0); +isFrozen( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + std::uint8_t depth = 0); [[nodiscard]] bool isAnyFrozen( ReadView const& view, std::initializer_list const& accounts, MPTIssue const& mptIssue, - int depth = 0); + std::uint8_t depth = 0); //------------------------------------------------------------------------------ // @@ -88,7 +92,7 @@ requireAuth( MPTIssue const& mptIssue, AccountID const& account, AuthType authType = AuthType::Legacy, - int depth = 0); + std::uint8_t depth = 0); /** Enforce account has MPToken to match its authorization. * @@ -104,22 +108,68 @@ enforceMPTokenAuthorization( XRPAmount const& priorBalance, beast::Journal j); -/** Check if the destination account is allowed - * to receive MPT. Return tecNO_AUTH if it doesn't - * and tesSUCCESS otherwise. +/** 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). + * + * @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. + * + * 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. + * + * @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( ReadView const& view, MPTIssue const& mptIssue, AccountID const& from, - AccountID const& to); + AccountID const& to, + WaiveMPTCanTransfer waive = WaiveMPTCanTransfer::No, + std::uint8_t depth = 0); -/** Check if Asset can be traded on DEX. return tecNO_PERMISSION - * if it doesn't and tesSUCCESS otherwise. +/** 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. + * + * @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); +canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth = 0); //------------------------------------------------------------------------------ // diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h index 17b0f7673e..2616a6d5c9 100644 --- a/include/xrpl/ledger/helpers/RippleStateHelpers.h +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -93,7 +93,7 @@ isFrozen(ReadView const& view, AccountID const& account, Issue const& issue) // Overload with depth parameter for uniformity with MPTIssue version. // The depth parameter is ignored for IOUs since they don't have vault recursion. [[nodiscard]] inline bool -isFrozen(ReadView const& view, AccountID const& account, Issue const& issue, int /*depth*/) +isFrozen(ReadView const& view, AccountID const& account, Issue const& issue, std::uint8_t /*depth*/) { return isFrozen(view, account, issue); } @@ -110,7 +110,7 @@ isDeepFrozen( ReadView const& view, AccountID const& account, Issue const& issue, - int = 0 /*ignored*/) + std::uint8_t = 0 /*ignored*/) { return isDeepFrozen(view, account, issue.currency, issue.account); } diff --git a/include/xrpl/ledger/helpers/TokenHelpers.h b/include/xrpl/ledger/helpers/TokenHelpers.h index 3d41ac47cd..b47286a5d5 100644 --- a/include/xrpl/ledger/helpers/TokenHelpers.h +++ b/include/xrpl/ledger/helpers/TokenHelpers.h @@ -34,6 +34,15 @@ enum class WaiveTransferFee : bool { No = false, Yes }; /** Controls whether accountSend is allowed to overflow OutstandingAmount **/ enum class AllowMPTOverflow : bool { No = false, Yes }; +/** 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, + * unwinding existing positions in SAV or the Lending Protocol. Has no + * effect on the IOU branch of canTransfer. + */ +enum class WaiveMPTCanTransfer : bool { No = false, Yes }; + /* Check if MPToken (for MPT) or trust line (for IOU) exists: * - StrongAuth - before checking if authorization is required * - WeakAuth @@ -63,7 +72,11 @@ isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& * purely defensive, as we currently do not allow such vaults to be created. */ [[nodiscard]] bool -isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0); +isFrozen( + ReadView const& view, + AccountID const& account, + Asset const& asset, + std::uint8_t depth = 0); [[nodiscard]] TER checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue); @@ -85,14 +98,14 @@ isAnyFrozen( ReadView const& view, std::initializer_list const& accounts, Asset const& asset, - int depth = 0); + std::uint8_t depth = 0); [[nodiscard]] bool isDeepFrozen( ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, - int depth = 0); + std::uint8_t depth = 0); /** * isFrozen check is recursive for MPT shares in a vault, descending to @@ -100,7 +113,11 @@ isDeepFrozen( * purely defensive, as we currently do not allow such vaults to be created. */ [[nodiscard]] bool -isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth = 0); +isDeepFrozen( + ReadView const& view, + AccountID const& account, + Asset const& asset, + std::uint8_t depth = 0); [[nodiscard]] TER checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue); @@ -234,7 +251,13 @@ requireAuth( AuthType authType = AuthType::Legacy); [[nodiscard]] TER -canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to); +canTransfer( + ReadView const& view, + Asset const& asset, + AccountID const& from, + AccountID const& to, + WaiveMPTCanTransfer waive = WaiveMPTCanTransfer::No, + std::uint8_t depth = 0); //------------------------------------------------------------------------------ // diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index c4b392a92f..19b8b44fe6 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -400,6 +400,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({ {sfPreviousTxnLgrSeq, SoeRequired}, {sfDomainID, SoeOptional}, {sfMutableFlags, SoeDefault}, + {sfReferenceHolding, SoeOptional}, })) /** A ledger object which tracks MPToken diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 5d1689dce9..01bb4fc480 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -205,6 +205,7 @@ TYPED_SFIELD(sfParentBatchID, UINT256, 36) TYPED_SFIELD(sfLoanBrokerID, UINT256, 37, SField::kSmdPseudoAccount | SField::kSmdDefault) TYPED_SFIELD(sfLoanID, UINT256, 38) +TYPED_SFIELD(sfReferenceHolding, UINT256, 39) // number (common) TYPED_SFIELD(sfNumber, NUMBER, 1) diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h index 8377fdf1a4..7f772b1c74 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h +++ b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h @@ -278,6 +278,30 @@ public: { return this->sle_->isFieldPresent(sfMutableFlags); } + + /** + * @brief Get sfReferenceHolding (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getReferenceHolding() const + { + if (hasReferenceHolding()) + return this->sle_->at(sfReferenceHolding); + return std::nullopt; + } + + /** + * @brief Check if sfReferenceHolding is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasReferenceHolding() const + { + return this->sle_->isFieldPresent(sfReferenceHolding); + } }; /** @@ -469,6 +493,17 @@ public: return *this; } + /** + * @brief Set sfReferenceHolding (SoeOptional) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceBuilder& + setReferenceHolding(std::decay_t const& value) + { + object_[sfReferenceHolding] = value; + return *this; + } + /** * @brief Build and return the completed MPTokenIssuance wrapper. * @param index The ledger entry index. diff --git a/include/xrpl/tx/invariants/MPTInvariant.h b/include/xrpl/tx/invariants/MPTInvariant.h index 7f405814e7..f2b0a18131 100644 --- a/include/xrpl/tx/invariants/MPTInvariant.h +++ b/include/xrpl/tx/invariants/MPTInvariant.h @@ -2,10 +2,13 @@ #include #include +#include #include #include #include +#include +#include namespace xrpl { @@ -20,6 +23,18 @@ 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. + bool referenceHoldingSetOnCreate_ = false; + + /// 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. + std::vector> deletedHoldings_; + public: void visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h index 3861c19afc..6c59d85548 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h @@ -6,23 +6,28 @@ namespace xrpl { +// NOLINTBEGIN(readability-redundant-member-init) struct MPTCreateArgs { std::optional priorBalance; AccountID const& account; std::uint32_t sequence = 0; std::uint32_t flags = 0; - std::optional maxAmount = - std::nullopt; // NOLINT(readability-redundant-member-init) - std::optional assetScale = - std::nullopt; // NOLINT(readability-redundant-member-init) - std::optional transferFee = - std::nullopt; // NOLINT(readability-redundant-member-init) + std::optional maxAmount = std::nullopt; + std::optional assetScale = std::nullopt; + std::optional transferFee = std::nullopt; std::optional const& metadata{}; - std::optional domainId = std::nullopt; // NOLINT(readability-redundant-member-init) - std::optional mutableFlags = - std::nullopt; // NOLINT(readability-redundant-member-init) + std::optional domainId = std::nullopt; + std::optional mutableFlags = std::nullopt; + // Set only by callers that issue an MPT representing a wrapped asset + // (e.g. VaultCreate's share token). The keylet must point to an + // existing MPToken or RippleState owned by `account`. Surfaces on + // the resulting MPTokenIssuance via the optional sfReferenceHolding + // field. Used by readers (canTransfer, canTrade, freezing) to + // inherit the underlying asset's transferability. + std::optional referenceHolding = std::nullopt; }; +// NOLINTEND(readability-redundant-member-init) class MPTokenIssuanceCreate : public Transactor { diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 4b67bb9867..c62d79dcac 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -57,19 +58,45 @@ isVaultPseudoAccountFrozen( ReadView const& view, AccountID const& account, MPTIssue const& mptShare, - int depth) + std::uint8_t depth) { if (!view.rules().enabled(featureSingleAssetVault)) return false; if (depth >= kMaxAssetCheckDepth) - return true; // LCOV_EXCL_LINE + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::View::isVaultPseudoAccountFrozen : reached asset check depth"); + return true; + // LCOV_EXCL_STOP + } auto const mptIssuance = view.read(keylet::mptIssuance(mptShare.getMptID())); if (mptIssuance == nullptr) return false; // zero MPToken won't block deletion of MPTokenIssuance auto const issuer = mptIssuance->getAccountID(sfIssuer); + + // Post-fixCleanup3_2_0: vault shares carry sfReferenceHolding pointing + // to the vault pseudo's MPToken or RippleState for the underlying. + // Read it to derive the underlying asset and recurse, skipping the + // issuer-account-then-vault chain. Pre-amendment shares (no field) + // fall back to the chain lookup below. + if (mptIssuance->isFieldPresent(sfReferenceHolding)) + { + auto const sleHolding = + view.read(keylet::unchecked(mptIssuance->getFieldH256(sfReferenceHolding))); + if (!sleHolding) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : dangling sfReferenceHolding"); + return false; + // LCOV_EXCL_STOP + } + return isAnyFrozen( + view, {issuer, account}, assetOfHolding(*mptIssuance, *sleHolding), depth + 1); + } + auto const mptIssuer = view.read(keylet::account(issuer)); if (mptIssuer == nullptr) { diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index 925f37c8ed..542a5b60ed 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -55,7 +55,11 @@ isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue cons } bool -isFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth) +isFrozen( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + std::uint8_t depth) { return isGlobalFrozen(view, mptIssue) || isIndividualFrozen(view, account, mptIssue) || isVaultPseudoAccountFrozen(view, account, mptIssue, depth); @@ -66,7 +70,7 @@ isAnyFrozen( ReadView const& view, std::initializer_list const& accounts, MPTIssue const& mptIssue, - int depth) + std::uint8_t depth) { if (isGlobalFrozen(view, mptIssue)) return true; @@ -303,7 +307,7 @@ requireAuth( MPTIssue const& mptIssue, AccountID const& account, AuthType authType, - int depth) + std::uint8_t depth) { auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); auto const sleIssuance = view.read(mptID); @@ -321,7 +325,12 @@ requireAuth( if (featureSAVEnabled) { if (depth >= kMaxAssetCheckDepth) - return tecINTERNAL; // LCOV_EXCL_LINE + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::MPTokenHelpers::requireAuth : reached asset check depth"); + return tecINTERNAL; + // LCOV_EXCL_STOP + } // requireAuth is recursive if the issuer is a vault pseudo-account auto const sleIssuer = view.read(keylet::account(mptIssuer)); @@ -489,28 +498,88 @@ enforceMPTokenAuthorization( // LCOV_EXCL_STOP } +[[nodiscard]] Asset +assetOfHolding(SLE const& sleShareIssuance, SLE const& sleHolding) +{ + XRPL_ASSERT_PARTS( + sleHolding.getType() == ltRIPPLE_STATE || sleHolding.getType() == ltMPTOKEN, + "xrpl::assetOfHolding", + "unexpected holding type"); + XRPL_ASSERT_PARTS( + sleShareIssuance.getType() == ltMPTOKEN_ISSUANCE, + "xrpl::assetOfHolding", + "not SLE MPTokenIssuance"); + + if (sleHolding.getType() == ltMPTOKEN) + return MPTIssue{sleHolding.getFieldH192(sfMPTokenIssuanceID)}; + + auto const vaultPseudo = sleShareIssuance.at(sfIssuer); + auto const lowLimit = sleHolding.getFieldAmount(sfLowLimit); + auto const highLimit = sleHolding.getFieldAmount(sfHighLimit); + auto const& iouIssuer = + (lowLimit.getIssuer() != vaultPseudo) ? lowLimit.getIssuer() : highLimit.getIssuer(); + return Issue{lowLimit.get().currency, iouIssuer}; +} + TER canTransfer( ReadView const& view, MPTIssue const& mptIssue, AccountID const& from, - AccountID const& to) + AccountID const& to, + WaiveMPTCanTransfer waive, + std::uint8_t depth) { auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); auto const sleIssuance = view.read(mptID); if (!sleIssuance) return tecOBJECT_NOT_FOUND; + auto const issuer = (*sleIssuance)[sfIssuer]; + if (waive == WaiveMPTCanTransfer::Yes || from == issuer || to == issuer) + return tesSUCCESS; + if (!sleIssuance->isFlag(lsfMPTCanTransfer)) + return TER{tecNO_AUTH}; + + // Post-fixCleanup3_2_0: vault shares carry sfReferenceHolding pointing + // to the vault pseudo's MPToken or RippleState for the underlying asset. + // Third-party transfers inherit the underlying's transferability. + // Issuer-involving transfers and waived callers returned tesSUCCESS above. + // + // The recursive call always passes WaiveMPTCanTransfer::No so that + // a waived outer caller does not transitively unlock the underlying. + if (view.rules().enabled(fixCleanup3_2_0) && sleIssuance->isFieldPresent(sfReferenceHolding)) { - if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer]) - return TER{tecNO_AUTH}; + // Defensive depth bound on the inheritance recursion. Unreachable + // in practice (vault-of-vault-shares is forbidden at VaultCreate). + if (depth >= kMaxAssetCheckDepth) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::MPTokenHelpers::canTransfer : reached asset check depth"); + return tecINTERNAL; + // LCOV_EXCL_STOP + } + + auto const sleHolding = + view.read(keylet::unchecked(sleIssuance->getFieldH256(sfReferenceHolding))); + if (!sleHolding) + return tefINTERNAL; // LCOV_EXCL_LINE + + return canTransfer( + view, + assetOfHolding(*sleIssuance, *sleHolding), + from, + to, + WaiveMPTCanTransfer::No, + depth + 1); } + return tesSUCCESS; } TER -canTrade(ReadView const& view, Asset const& asset) +canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth) { return asset.visit( [&](Issue const&) -> TER { return tesSUCCESS; }, @@ -520,6 +589,31 @@ canTrade(ReadView const& view, Asset const& asset) return tecOBJECT_NOT_FOUND; if (!sleIssuance->isFlag(lsfMPTCanTrade)) return tecNO_PERMISSION; + + // Post-fixCleanup3_2_0: vault shares inherit the underlying + // asset's tradability. A share whose underlying has been + // removed from trading cannot itself be placed on the DEX. + if (view.rules().enabled(fixCleanup3_2_0) && + sleIssuance->isFieldPresent(sfReferenceHolding)) + { + // Defensive depth bound on the inheritance recursion. + // Unreachable in practice (vault-of-vault-shares + // forbidden at VaultCreate). + if (depth >= kMaxAssetCheckDepth) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::MPTokenHelpers::canTrade : reached asset check depth"); + return tecINTERNAL; + // LCOV_EXCL_STOP + } + auto const sleHolding = + view.read(keylet::unchecked(sleIssuance->getFieldH256(sfReferenceHolding))); + if (!sleHolding) + return tefINTERNAL; // LCOV_EXCL_LINE + + return canTrade(view, assetOfHolding(*sleIssuance, *sleHolding), depth + 1); + } + return tesSUCCESS; }); } @@ -892,7 +986,12 @@ issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amo } static TER -checkMPTAllowed(ReadView const& view, TxType txType, Asset const& asset, AccountID const& accountID) +checkMPTAllowed( + ReadView const& view, + TxType txType, + Asset const& asset, + AccountID const& accountID, + std::uint8_t depth = 0) { if (!asset.holds()) return tesSUCCESS; @@ -914,17 +1013,15 @@ checkMPTAllowed(ReadView const& view, TxType txType, Asset const& asset, Account if (!issuanceSle) return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE - auto const flags = issuanceSle->getFlags(); - - if ((flags & lsfMPTLocked) != 0u) + if (issuanceSle->isFlag(lsfMPTLocked)) return tecLOCKED; // LCOV_EXCL_LINE // Offer crossing and Payment - if ((flags & lsfMPTCanTrade) == 0) + if (!issuanceSle->isFlag(lsfMPTCanTrade)) return tecNO_PERMISSION; if (accountID != issuer) { - if ((flags & lsfMPTCanTransfer) == 0) + if (!issuanceSle->isFlag(lsfMPTCanTransfer)) return tecNO_PERMISSION; auto const mptSle = view.read(keylet::mptoken(issuanceKey.key, accountID)); @@ -935,6 +1032,34 @@ checkMPTAllowed(ReadView const& view, TxType txType, Asset const& asset, Account if (mptSle->isFlag(lsfMPTLocked)) return tecLOCKED; + + // Post-fixCleanup3_2_0: vault shares inherit the underlying + // asset's checks here too. Without this, a share could be + // placed on the AMM, in an Offer, or in a Check even after + // the issuer has restricted the underlying. Mirrors the + // canTransfer / canTrade inheritance for path-find-adjacent + // operations that don't go through canTransfer directly. + if (view.rules().enabled(fixCleanup3_2_0) && + issuanceSle->isFieldPresent(sfReferenceHolding)) + { + // Defensive depth bound on the inheritance recursion. + // Reachable only post-fixCleanup3_2_0 and unreachable in + // practice (vault-of-vault-shares forbidden at VaultCreate). + if (depth >= kMaxAssetCheckDepth) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::MPTokenHelpers::checkMPTAllowed : reached asset check depth"); + return tecINTERNAL; + // LCOV_EXCL_STOP + } + auto const sleHolding = + view.read(keylet::unchecked(issuanceSle->getFieldH256(sfReferenceHolding))); + if (!sleHolding) + return tefINTERNAL; // LCOV_EXCL_LINE + + return checkMPTAllowed( + view, txType, assetOfHolding(*issuanceSle, *sleHolding), accountID, depth + 1); + } } return tesSUCCESS; diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index c9dccb884d..71019761ed 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -63,7 +63,7 @@ isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& } bool -isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth) +isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, std::uint8_t depth) { return std::visit( [&](auto const& issue) { return isFrozen(view, account, issue, depth); }, asset.value()); @@ -107,7 +107,7 @@ isAnyFrozen( ReadView const& view, std::initializer_list const& accounts, Asset const& asset, - int depth) + std::uint8_t depth) { return asset.visit( [&](Issue const& issue) { return isAnyFrozen(view, accounts, issue); }, @@ -115,7 +115,11 @@ isAnyFrozen( } bool -isDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth) +isDeepFrozen( + ReadView const& view, + AccountID const& account, + MPTIssue const& mptIssue, + std::uint8_t depth) { // Unlike IOUs, frozen / locked MPTs are not allowed to send or receive // funds, so checking "deep frozen" is the same as checking "frozen". @@ -123,7 +127,7 @@ isDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mpt } bool -isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth) +isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, std::uint8_t depth) { return std::visit( [&](auto const& issue) { return isDeepFrozen(view, account, issue, depth); }, @@ -500,13 +504,19 @@ requireAuth(ReadView const& view, Asset const& asset, AccountID const& account, } TER -canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to) +canTransfer( + ReadView const& view, + Asset const& asset, + AccountID const& from, + AccountID const& to, + WaiveMPTCanTransfer waive, + std::uint8_t depth) { - return std::visit( - [&](TIss const& issue) -> TER { - return canTransfer(view, issue, from, to); + return asset.visit( + [&](MPTIssue const& issue) -> TER { + return canTransfer(view, issue, from, to, waive, depth); }, - asset.value()); + [&](Issue const& issue) -> TER { return canTransfer(view, issue, from, to); }); } //------------------------------------------------------------------------------ diff --git a/src/libxrpl/tx/invariants/MPTInvariant.cpp b/src/libxrpl/tx/invariants/MPTInvariant.cpp index 43379b0b86..ecba0b6227 100644 --- a/src/libxrpl/tx/invariants/MPTInvariant.cpp +++ b/src/libxrpl/tx/invariants/MPTInvariant.cpp @@ -5,11 +5,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -30,6 +32,13 @@ ValidMPTIssuance::visitEntry( std::shared_ptr const& before, std::shared_ptr const& after) { + // The sfReferenceHolding tracking and the deleted-holding capture are + // only meaningful post-fixCleanup3_2_0 (the field is never set + // pre-amendment, and the holding-deletion rule does not apply). + // Skip both blocks when the amendment is off so we avoid wasted work + // on the hot path. + bool const fix320Enabled = isFeatureEnabled(fixCleanup3_2_0); + if (after && after->getType() == ltMPTOKEN_ISSUANCE) { if (isDelete) @@ -39,6 +48,21 @@ ValidMPTIssuance::visitEntry( else if (!before) { mptIssuancesCreated_++; + if (fix320Enabled && after->isFieldPresent(sfReferenceHolding)) + referenceHoldingSetOnCreate_ = true; + } + else if (fix320Enabled) + { + // Modified issuance: detect any change to sfReferenceHolding. + bool const beforePresent = before->isFieldPresent(sfReferenceHolding); + bool const afterPresent = after->isFieldPresent(sfReferenceHolding); + if (beforePresent != afterPresent || + (afterPresent && + before->getFieldH256(sfReferenceHolding) != + after->getFieldH256(sfReferenceHolding))) + { + referenceHoldingMutated_ = true; + } } } @@ -47,6 +71,8 @@ ValidMPTIssuance::visitEntry( if (isDelete) { mptokensDeleted_++; + if (fix320Enabled) + deletedHoldings_.push_back(after); } else if (!before) { @@ -56,6 +82,11 @@ ValidMPTIssuance::visitEntry( mptCreatedByIssuer_ = true; } } + + // Capture deleted RippleState SLEs so finalize() can verify none of + // them were owned by a vault pseudo-account outside VaultDelete. + if (fix320Enabled && isDelete && after && after->getType() == ltRIPPLE_STATE) + deletedHoldings_.push_back(after); } bool @@ -68,6 +99,63 @@ ValidMPTIssuance::finalize( { auto const& rules = view.rules(); bool const mptV2Enabled = rules.enabled(featureMPTokensV2); + + // Post-fixCleanup3_2_0: + // - sfReferenceHolding is set only by VaultCreate at share-issuance + // creation, and is immutable thereafter. + // - A vault pseudo-account's MPToken or RippleState may only be + // deleted by VaultDelete; the share's sfReferenceHolding pointer + // must not dangle outside that controlled lifecycle. + if (rules.enabled(fixCleanup3_2_0)) + { + bool invariantPasses = true; + if (referenceHoldingMutated_) + { + JLOG(j.fatal()) << "Invariant failed: sfReferenceHolding was modified " + "on an existing MPTokenIssuance"; + invariantPasses = false; + } + if (referenceHoldingSetOnCreate_ && tx.getTxnType() != ttVAULT_CREATE) + { + JLOG(j.fatal()) << "Invariant failed: sfReferenceHolding set on a new " + "MPTokenIssuance by a non-VaultCreate transaction"; + invariantPasses = false; + } + if (!deletedHoldings_.empty() && tx.getTxnType() != ttVAULT_DELETE) + { + auto const isVaultPseudo = [&](AccountID const& acct) { + auto const sle = view.read(keylet::account(acct)); + return sle && sle->isFieldPresent(sfVaultID); + }; + for (auto const& sleHolding : deletedHoldings_) + { + bool offending = false; + if (sleHolding->getType() == ltMPTOKEN) + { + offending = isVaultPseudo(sleHolding->at(sfAccount)); + } + else // ltRIPPLE_STATE + { + auto const lowLimit = sleHolding->getFieldAmount(sfLowLimit); + auto const highLimit = sleHolding->getFieldAmount(sfHighLimit); + // Each limit's STAmount.issuer is the COUNTERPARTY of + // that side's owner: lowLimit's issuer is the high + // account, highLimit's issuer is the low account. + offending = + isVaultPseudo(lowLimit.getIssuer()) || isVaultPseudo(highLimit.getIssuer()); + } + if (offending) + { + JLOG(j.fatal()) << "Invariant failed: vault pseudo-account holding " + "deleted by a non-VaultDelete transaction"; + invariantPasses = false; + } + } + } + if (!invariantPasses) + return false; + } + if (isTesSuccess(result) || (mptV2Enabled && result == tecINCOMPLETE)) { [[maybe_unused]] diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp index 17a9683690..331c44b1e8 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -95,8 +96,13 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx) // The broker's pseudo-account is the source of funds. auto const pseudoAccountID = sleBroker->at(sfAccount); - // Cannot transfer a non-transferable Asset - if (auto const ret = canTransfer(ctx.view, vaultAsset, pseudoAccountID, dstAcct)) + // Post-fixCleanup3_2_0: cover withdraw is a recovery path that bypasses + // the lsfMPTCanTransfer flag check, so an issuer cannot trap a broker's + // first-loss capital. Other transferability checks (IOU NoRipple, freeze, + // requireAuth) still apply. + auto const waive = ctx.view.rules().enabled(fixCleanup3_2_0) ? WaiveMPTCanTransfer::Yes + : WaiveMPTCanTransfer::No; + if (auto const ret = canTransfer(ctx.view, vaultAsset, pseudoAccountID, dstAcct, waive)) return ret; // Withdrawal to a 3rd party destination account is essentially a transfer. diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 1440b67309..64d0b01f5e 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,11 @@ MPTokenIssuanceCreate::getFlagsMask(PreflightContext const& ctx) NotTEC MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) { + // sfReferenceHolding is set only internally by VaultCreate. Reject + // any user-submitted MPTokenIssuanceCreate that attempts to carry it. + if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx.isFieldPresent(sfReferenceHolding)) + return temMALFORMED; + // If the mutable flags field is included, at least one flag must be // specified. if (auto const mutableFlags = ctx.tx[~sfMutableFlags]; mutableFlags && @@ -141,6 +147,22 @@ MPTokenIssuanceCreate::create(ApplyView& view, beast::Journal journal, MPTCreate if (args.mutableFlags) (*mptIssuance)[sfMutableFlags] = *args.mutableFlags; + if (args.referenceHolding) + { + // Defensive: the holding must already exist and be of an + // expected type. Callers (currently only VaultCreate) + // 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)); + if (!sleHolding) + return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + auto const type = sleHolding->getType(); + if (type != ltMPTOKEN && type != ltRIPPLE_STATE) + return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + (*mptIssuance)[sfReferenceHolding] = *args.referenceHolding; + } + view.insert(mptIssuance); } diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index c6e1f28d57..7490f44619 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -1,12 +1,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -162,9 +164,9 @@ VaultCreate::doApply() auto maybePseudo = createPseudoAccount(view(), vault->key(), sfVaultID); if (!maybePseudo) return maybePseudo.error(); // LCOV_EXCL_LINE - auto& pseudo = *maybePseudo; - auto pseudoId = pseudo->at(sfAccount); - auto asset = tx[sfAsset]; + auto const& pseudo = *maybePseudo; + AccountID const pseudoId = pseudo->at(sfAccount); + auto const asset = tx[sfAsset]; if (auto ter = addEmptyHolding(view(), pseudoId, preFeeBalance_, asset, j_); !isTesSuccess(ter)) return ter; @@ -182,13 +184,24 @@ VaultCreate::doApply() // Note, here we are **not** creating an MPToken for the assets held in // the vault. That MPToken or TrustLine/RippleState is created above, in // addEmptyHolding. Here we are creating MPTokenIssuance for the shares - // in the vault - auto maybeShare = MPTokenIssuanceCreate::create( + // in the vault. + // + // Post-fixCleanup3_2_0: surface the vault pseudo's holding (MPToken + // for MPT, RippleState for IOU) on the share via sfReferenceHolding. + // XRP underlyings leave it unset. + auto const referenceHolding = [&]() -> std::optional { + if (!view().rules().enabled(fixCleanup3_2_0) || asset.native()) + return std::nullopt; + return asset.holds() + ? keylet::mptoken(asset.get().getMptID(), pseudoId).key + : keylet::line(pseudoId, asset.get()).key; + }(); + auto const maybeShare = MPTokenIssuanceCreate::create( view(), j_, { .priorBalance = std::nullopt, - .account = pseudoId->value(), + .account = pseudoId, .sequence = 1, .flags = mptFlags, .assetScale = scale, @@ -196,6 +209,7 @@ VaultCreate::doApply() .metadata = tx[~sfMPTokenMetadata], .domainId = tx[~sfDomainID], .mutableFlags = std::nullopt, + .referenceHolding = referenceHolding, }); if (!maybeShare) return maybeShare.error(); // LCOV_EXCL_LINE diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index 136f9e17c6..ced82f6735 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -65,7 +65,14 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx) auto const& vaultAccount = vault->at(sfAccount); auto const& account = ctx.tx[sfAccount]; auto const& dstAcct = ctx.tx[~sfDestination].value_or(account); - if (auto ter = canTransfer(ctx.view, vaultAsset, vaultAccount, dstAcct); !isTesSuccess(ter)) + // Post-fixCleanup3_2_0: withdraw is a recovery path that bypasses the + // lsfMPTCanTransfer flag check, so an issuer cannot trap depositor funds. + // Other transferability checks (IOU NoRipple, freeze, requireAuth) still + // apply. + auto const waive = ctx.view.rules().enabled(fixCleanup3_2_0) ? WaiveMPTCanTransfer::Yes + : WaiveMPTCanTransfer::No; + if (auto ter = canTransfer(ctx.view, vaultAsset, vaultAccount, dstAcct, waive); + !isTesSuccess(ter)) { JLOG(ctx.j.debug()) << "VaultWithdraw: vault assets are non-transferable."; return ter; diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 775e18656b..21094e0142 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -1783,7 +1783,7 @@ class Invariants_test : public beast::unit_test::Suite for (std::size_t n = 0; n < numCreds; ++n) { auto credType = "cred_type" + std::to_string(n); - credentials.push_back({a2, credType}); + credentials.push_back({.issuer = a2, .credType = credType}); } std::uint32_t const seq = env.seq(a1); @@ -4103,6 +4103,114 @@ class Invariants_test : public beast::unit_test::Suite return true; }); } + + // sfReferenceHolding can only be set on creation by VaultCreate. A + // non-VaultCreate transaction that creates an MPTokenIssuance with + // sfReferenceHolding present must trip the invariant. + doInvariantCheck( + {{"sfReferenceHolding set on a new MPTokenIssuance by a " + "non-VaultCreate transaction"}}, + [](Account const& a1, Account const&, ApplyContext& ac) { + auto const sleAcct = ac.view().peek(keylet::account(a1.id())); + if (!sleAcct) + return false; + MPTIssue const mpt{makeMptID(sleAcct->getFieldU32(sfSequence), a1)}; + auto sleNew = std::make_shared(keylet::mptIssuance(mpt.getMptID())); + sleNew->setFieldH256(sfReferenceHolding, uint256{1}); + ac.view().insert(sleNew); + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_SET, [](STObject&) {}}); + + // sfReferenceHolding is immutable: changing the field on an + // existing MPTokenIssuance must trip the invariant. Set up a real + // vault via preclose (so the share issuance carries + // sfReferenceHolding), then mutate it in precheck to produce a + // before/after pair. + { + uint256 vaultKey; + doInvariantCheck( + {{"sfReferenceHolding was modified on an existing " + "MPTokenIssuance"}}, + [&](Account const&, Account const&, ApplyContext& ac) { + auto const sleVault = ac.view().peek(keylet::vault(vaultKey)); + if (!sleVault) + return false; + auto sleIssuance = + ac.view().peek(keylet::mptIssuance(sleVault->at(sfShareMPTID))); + if (!sleIssuance) + return false; + sleIssuance->setFieldH256(sfReferenceHolding, uint256{2}); + ac.view().update(sleIssuance); + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_SET, [](STObject&) {}}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + [&](Account const& a1, Account const&, Env& env) { + Account const issuer{"issuer"}; + env.fund(XRP(10'000), issuer); + env.close(); + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = a1}); + env.close(); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = a1, .asset = asset}); + env(tx); + env.close(); + vaultKey = keylet.key; + return true; + }); + } + + // A vault pseudo-account's MPToken cannot be deleted by anything + // other than a VaultDelete transaction. Set up a vault, then have + // an arbitrary tx erase the pseudo's MPToken in precheck. + { + uint256 vaultKey; + doInvariantCheck( + {{"vault pseudo-account holding deleted by a " + "non-VaultDelete transaction"}}, + [&](Account const&, Account const&, ApplyContext& ac) { + auto const sleVault = ac.view().peek(keylet::vault(vaultKey)); + if (!sleVault) + return false; + auto const sleIssuance = + ac.view().peek(keylet::mptIssuance(sleVault->at(sfShareMPTID))); + if (!sleIssuance || !sleIssuance->isFieldPresent(sfReferenceHolding)) + return false; + auto sleHolding = ac.view().peek( + keylet::unchecked(sleIssuance->getFieldH256(sfReferenceHolding))); + if (!sleHolding) + return false; + ac.view().erase(sleHolding); + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_SET, [](STObject&) {}}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + [&](Account const& a1, Account const&, Env& env) { + Account const issuer{"issuer"}; + env.fund(XRP(10'000), issuer); + env.close(); + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = a1}); + env.close(); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = a1, .asset = asset}); + env(tx); + env.close(); + vaultKey = keylet.key; + return true; + }); + } } // Test the invariant overwrite fix for both pre- and post-amendment diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index a8132887fc..201bb6942a 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -5254,7 +5255,7 @@ protected: auto const loanSequence = brokerStateBefore->at(sfLoanSequence); auto const keylet = keylet::loan(broker.brokerID, loanSequence); - auto const closeStartDate = (parentCloseTime() / 10 + 1) * 10; + auto const closeStartDate = ((parentCloseTime() / 10) + 1) * 10; auto const grace = 5'000; auto const interval = kMaxTime - closeStartDate - grace; auto const total = 1; @@ -5283,7 +5284,7 @@ protected: env(pay(issuer, borrower, iouAsset(Number{1'055'524'81, -2}))); // Start date when the ledger is closed will be larger - auto const closeStartDate = (parentCloseTime() / 10 + 1) * 10; + auto const closeStartDate = ((parentCloseTime() / 10) + 1) * 10; auto const grace = 5'000; auto const maxLoanTime = kMaxTime - closeStartDate - grace; auto const total = [&]() { @@ -5397,7 +5398,7 @@ protected: void testCoverDepositWithdrawNonTransferableMPT(FeatureBitset feature) { - testcase("CoverDeposit and CoverWithdraw reject MPT without CanTransfer"); + testcase("CoverDeposit blocked, CoverWithdraw allowed when CanTransfer cleared"); using namespace jtx; using namespace loanBroker; @@ -5409,19 +5410,14 @@ protected: env.fund(XRP(100'000), issuer, alice); env.close(); - MPTTester mpt{env, issuer, kMptInitNoFund}; - - mpt.create({.flags = tfMPTCanTransfer, .mutableFlags = tmfMPTCanMutateCanTransfer}); - - env.close(); - + MPTTester mpt( + {.env = env, + .issuer = issuer, + .holders = {alice}, + .pay = 100, + .flags = tfMPTCanTransfer, + .mutableFlags = tmfMPTCanMutateCanTransfer}); PrettyAsset const asset = mpt["MPT"]; - mpt.authorize({.account = alice}); - env.close(); - - // Issuer can fund the holder even if CanTransfer is not set. - env(pay(issuer, alice, asset(100))); - env.close(); Vault const vault{env}; auto const [createTx, vaultKeylet] = vault.create({.owner = alice, .asset = asset}); @@ -5438,30 +5434,9 @@ protected: Account const pseudoAccount{"Loan Broker pseudo-account", brokerSle->at(sfAccount)}; - // Remove CanTransfer after the broker is set up. - mpt.set({.mutableFlags = tmfMPTClearCanTransfer}); - env.close(); - - // Standard Payment path should forbid third-party transfers. - auto const err = feature[featureMPTokensV2] ? tecNO_PERMISSION : tecNO_AUTH; - env(pay(alice, pseudoAccount, asset(1)), Ter(err)); - env.close(); - - // Cover cannot be transferred to broker account + // First, deposit some cover while CanTransfer is set so we have an + // existing position to withdraw from after the governance action. auto const depositAmount = asset(1); - env(coverDeposit(alice, brokerKeylet.key, depositAmount), Ter{tecNO_AUTH}); - env.close(); - - if (auto const refreshed = env.le(brokerKeylet); BEAST_EXPECT(refreshed)) - { - BEAST_EXPECT(refreshed->at(sfCoverAvailable) == 0); - env.require(Balance(pseudoAccount, asset(0))); - } - - // Set CanTransfer again and transfer some deposit - mpt.set({.mutableFlags = tmfMPTSetCanTransfer}); - env.close(); - env(coverDeposit(alice, brokerKeylet.key, depositAmount)); env.close(); @@ -5471,26 +5446,177 @@ protected: env.require(Balance(pseudoAccount, depositAmount)); } - // Remove CanTransfer after the deposit + // Issuer governance: clear CanTransfer. mpt.set({.mutableFlags = tmfMPTClearCanTransfer}); env.close(); - // Cover cannot be transferred from broker account - env(coverWithdraw(alice, brokerKeylet.key, depositAmount), Ter{tecNO_AUTH}); + // Standard Payment path still forbids third-party transfers. + auto const err = feature[featureMPTokensV2] ? tecNO_PERMISSION : tecNO_AUTH; + env(pay(alice, pseudoAccount, asset(1)), Ter(err)); env.close(); - // Set CanTransfer again and withdraw - mpt.set({.mutableFlags = tmfMPTSetCanTransfer}); - env.close(); - - env(coverWithdraw(alice, brokerKeylet.key, depositAmount)); + // New cover deposits are blocked - this would create new exposure. + env(coverDeposit(alice, brokerKeylet.key, depositAmount), Ter{tecNO_AUTH}); env.close(); if (auto const refreshed = env.le(brokerKeylet); BEAST_EXPECT(refreshed)) { - BEAST_EXPECT(refreshed->at(sfCoverAvailable) == 0); - env.require(Balance(pseudoAccount, asset(0))); + BEAST_EXPECT(refreshed->at(sfCoverAvailable) == 1); + env.require(Balance(pseudoAccount, depositAmount)); } + + bool const postAmendment = feature[fixCleanup3_2_0]; + if (postAmendment) + { + // Post-fixCleanup3_2_0: existing cover can always be withdrawn + // even when CanTransfer is cleared, so the broker is not trapped. + env(coverWithdraw(alice, brokerKeylet.key, depositAmount)); + env.close(); + + if (auto const refreshed = env.le(brokerKeylet); BEAST_EXPECT(refreshed)) + { + BEAST_EXPECT(refreshed->at(sfCoverAvailable) == 0); + env.require(Balance(pseudoAccount, asset(0))); + } + } + else + { + // Pre-fixCleanup3_2_0 regression: cover withdraw was blocked, + // trapping the broker's first-loss capital. + env(coverWithdraw(alice, brokerKeylet.key, depositAmount), Ter{tecNO_AUTH}); + env.close(); + + if (auto const refreshed = env.le(brokerKeylet); BEAST_EXPECT(refreshed)) + { + BEAST_EXPECT(refreshed->at(sfCoverAvailable) == 1); + env.require(Balance(pseudoAccount, depositAmount)); + } + } + } + + void + testLoanSetBlockedLoanPayAllowedWhenCanTransferCleared() + { + testcase("LoanSet blocked, LoanPay allowed when CanTransfer cleared"); + using namespace jtx; + using namespace loan; + + Env env(*this, all_); + + Account const issuer{"issuer"}; + Account const lender{"lender"}; + Account const borrower{"borrower"}; + + env.fund(XRP(1'000'000), issuer, lender, borrower); + env.close(); + + MPTTester mpt( + {.env = env, + .issuer = issuer, + .holders = {lender, borrower}, + .flags = tfMPTCanTransfer | tfMPTCanLock, + .mutableFlags = tmfMPTCanMutateCanTransfer}); + PrettyAsset const asset = mpt.issuanceID(); + env(pay(issuer, lender, asset(10'000'000))); + // Fund the borrower with enough to cover principal+interest+fees + env(pay(issuer, borrower, asset(100'000))); + env.close(); + + // Create vault and broker while CanTransfer is set. + auto const broker = createVaultAndBroker(env, asset, lender); + + auto const loanSetFee = Fee(env.current()->fees().base * 2); + + // Create an existing loan while CanTransfer is set. + env(set(borrower, broker.brokerID, 1'000), + Sig(sfCounterpartySignature, lender), + loanSetFee); + env.close(); + auto const loanKeylet = keylet::loan(broker.brokerID, 1); + BEAST_EXPECT(env.le(loanKeylet)); + + // Issuer governance: clear CanTransfer. + mpt.set({.mutableFlags = tmfMPTClearCanTransfer}); + env.close(); + + // Issuing a NEW loan is blocked - it would create new exposure into + // a pool the issuer is restricting. + env(set(borrower, broker.brokerID, 1'000), + Sig(sfCounterpartySignature, lender), + loanSetFee, + Ter{tecNO_AUTH}); + env.close(); + + // Repaying an existing loan is always allowed - blocking it would + // create irrecoverable bad debt and trap SAV depositor principal. + env(pay(borrower, loanKeylet.key, asset(1'000))); + env.close(); + } + + void + testLendingCanTradeClearedNoImpact() + { + testcase("Lending: CanTrade cleared has no impact"); + using namespace jtx; + using namespace loan; + using namespace loanBroker; + + Env env(*this, all_); + + Account const issuer{"issuer"}; + Account const lender{"lender"}; + Account const borrower{"borrower"}; + + env.fund(XRP(1'000'000), issuer, lender, borrower); + env.close(); + + MPTTester mpt( + {.env = env, + .issuer = issuer, + .holders = {lender, borrower}, + .flags = tfMPTCanTransfer | tfMPTCanTrade | tfMPTCanLock, + .mutableFlags = tmfMPTCanMutateCanTrade}); + PrettyAsset const asset = mpt.issuanceID(); + env(pay(issuer, lender, asset(10'000'000))); + env(pay(issuer, borrower, asset(100'000))); + env.close(); + + auto const broker = createVaultAndBroker(env, asset, lender); + + // Sanity: while CanTrade is set, the asset can be placed on the DEX. + env(offer(lender, XRP(1), asset(10))); + env.close(); + + // Issuer governance: clear CanTrade. Loan origination and repayment + // are not trades: nothing in the Lending Protocol should be impacted. + mpt.set({.mutableFlags = tmfMPTClearCanTrade}); + env.close(); + + // Control: clearing CanTrade is observable on the DEX path. + env(offer(lender, XRP(1), asset(10)), Ter{tecNO_PERMISSION}); + env.close(); + + auto const loanSetFee = Fee(env.current()->fees().base * 2); + + // New cover deposits still work. + env(coverDeposit(lender, broker.brokerID, asset(100))); + env.close(); + + // New loan issuance still works. + env(loan::set(borrower, broker.brokerID, 1'000), + Sig(sfCounterpartySignature, lender), + loanSetFee); + env.close(); + auto const loanKeylet = keylet::loan(broker.brokerID, 1); + BEAST_EXPECT(env.le(loanKeylet)); + + // Repayment still works. + env(pay(borrower, loanKeylet.key, asset(1'000))); + env.close(); + + // Cover withdrawal still works. + env(coverWithdraw(lender, broker.brokerID, asset(100))); + env.close(); } #if LOAN_TODO @@ -6882,7 +7008,7 @@ protected: auto credType = "credential1"; - pdomain::Credentials const credentials1{{issuer, credType}}; + pdomain::Credentials const credentials1{{.issuer = issuer, .credType = credType}}; env(pdomain::setTx(issuer, credentials1)); env.close(); @@ -6985,7 +7111,7 @@ protected: auto credType = "credential1"; - pdomain::Credentials const credentials1{{issuer, credType}}; + pdomain::Credentials const credentials1{{.issuer = issuer, .credType = credType}}; env(pdomain::setTx(issuer, credentials1)); env.close(); @@ -7631,6 +7757,9 @@ public: auto const all = jtx::testableAmendments(); testCoverDepositWithdrawNonTransferableMPT(all); testCoverDepositWithdrawNonTransferableMPT(all - featureMPTokensV2); + testCoverDepositWithdrawNonTransferableMPT(all - fixCleanup3_2_0); + testLoanSetBlockedLoanPayAllowedWhenCanTransferCleared(); + testLendingCanTradeClearedNoImpact(); testPoCUnsignedUnderflowOnFullPayAfterEarlyPeriodic(); testDisabled(); diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 1e7763ac62..0e48db6080 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -191,6 +192,22 @@ class MPToken_test : public beast::unit_test::Suite .metadata = "test", .err = temMALFORMED}); } + + // sfReferenceHolding is populated internally only by VaultCreate. + // A user-submitted MPTokenIssuanceCreate carrying the field must be + // rejected at preflight under fixCleanup3_2_0. + if (features[fixCleanup3_2_0]) + { + Env env{*this, features}; + env.fund(XRP(1'000), alice); + env.close(); + + json::Value jv; + jv[sfAccount] = alice.human(); + jv[sfTransactionType] = jss::MPTokenIssuanceCreate; + jv[sfReferenceHolding] = to_string(uint256{1}); + env(jv, Ter(temMALFORMED)); + } } void @@ -1777,7 +1794,7 @@ class MPToken_test : public beast::unit_test::Suite env.close(); // Bob authorize credentials - env(deposit::authCredentials(bob, {{dpIssuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer = dpIssuer, .credType = credType}})); env.close(); // alice try to send 100 MPT to bob, not authorized diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 318a6dc45c..af6f4841f4 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -2241,7 +2242,7 @@ class Vault_test : public beast::unit_test::Suite PrettyAsset const& asset, Vault& vault, MPTTester& mptt) { - testcase("MPT non-transferable"); + testcase("MPT non-transferable: block deposit, allow withdraw"); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); env(tx); @@ -2251,22 +2252,19 @@ class Vault_test : public beast::unit_test::Suite env(tx); env.close(); - // Remove CanTransfer + // Issuer governance: clear CanTransfer. New exposure must be + // blocked, but recovery paths must remain open so existing + // depositors are not trapped. mptt.set({.mutableFlags = tmfMPTClearCanTransfer}); env.close(); + // New deposit is blocked. env(tx, Ter{tecNO_AUTH}); env.close(); + // Existing depositor can always withdraw, even though the asset + // is no longer freely transferable. tx = vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}); - - env(tx, Ter{tecNO_AUTH}); - env.close(); - - // Restore CanTransfer - mptt.set({.mutableFlags = tmfMPTSetCanTransfer}); - env.close(); - env(tx); env.close(); @@ -2274,6 +2272,245 @@ class Vault_test : public beast::unit_test::Suite env(vault.del({.owner = owner, .id = keylet.key})); }); + { + testcase("MPT non-transferable: pre-fixCleanup3_2_0 withdraw blocked"); + + // Regression: before fixCleanup3_2_0 a depositor was trapped if + // the issuer cleared lsfMPTCanTransfer. Verify that the legacy + // (broken) behavior is preserved when the amendment is disabled. + Env env{*this, testableAmendments() - fixCleanup3_2_0}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + env.fund(XRP(10'000), issuer, owner, depositor); + env.close(); + Vault const vault{env}; + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create( + {.flags = tfMPTCanTransfer | tfMPTCanLock, + .mutableFlags = tmfMPTCanMutateCanTransfer}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + mptt.authorize({.account = depositor}); + env(pay(issuer, depositor, asset(1'000))); + env.close(); + + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)})); + env.close(); + + mptt.set({.mutableFlags = tmfMPTClearCanTransfer}); + env.close(); + + // Pre-amendment: deposit blocked (matches new behavior). + env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)}), + Ter{tecNO_AUTH}); + env.close(); + + // Pre-amendment: withdraw is also blocked - this is the bug + // that fixCleanup3_2_0 fixes. + env(vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(100)}), + Ter{tecNO_AUTH}); + env.close(); + } + + { + testcase("MPT non-transferable: vault shares inherit restriction"); + + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + env.fund(XRP(10'000), issuer, owner, alice, bob); + env.close(); + Vault const vault{env}; + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create( + {.flags = tfMPTCanTransfer | tfMPTCanLock, + .mutableFlags = tmfMPTCanMutateCanTransfer}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + mptt.authorize({.account = alice}); + mptt.authorize({.account = bob}); + env(pay(issuer, alice, asset(1'000))); + env(pay(issuer, bob, asset(1'000))); + env.close(); + + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(500)})); + // Bob also deposits so he has a share MPToken to receive into. + env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(500)})); + env.close(); + + auto const shares = [&]() -> PrettyAsset { + auto const sle = env.le(keylet); + BEAST_EXPECT(sle != nullptr); + return MPTIssue(sle->at(sfShareMPTID)); + }(); + + // Sanity: while CanTransfer is set on the underlying, peer-to-peer + // share transfers are allowed. + env(pay(alice, bob, shares(1))); + env.close(); + + // Issuer governance: clear CanTransfer on the underlying. + mptt.set({.mutableFlags = tmfMPTClearCanTransfer}); + env.close(); + + // Vault shares inherit the restriction: third-party share-to-share + // payments are blocked. + env(pay(alice, bob, shares(1)), Ter{tecNO_AUTH}); + env.close(); + + // Recovery path: existing share holders can still redeem shares + // for the underlying asset via VaultWithdraw. + env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = shares(1)})); + env.close(); + } + + { + testcase("MPT non-transferable: pre-fixCleanup3_2_0 share transfer succeeds"); + + // Regression: before fixCleanup3_2_0 a peer-to-peer share Payment + // succeeded even when the underlying asset's lsfMPTCanTransfer + // was cleared. Verify that the legacy (non-inheriting) behavior + // is preserved when the amendment is disabled. + Env env{*this, testableAmendments() - fixCleanup3_2_0}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + env.fund(XRP(10'000), issuer, owner, alice, bob); + env.close(); + Vault const vault{env}; + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create( + {.flags = tfMPTCanTransfer | tfMPTCanLock, + .mutableFlags = tmfMPTCanMutateCanTransfer}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + mptt.authorize({.account = alice}); + mptt.authorize({.account = bob}); + env(pay(issuer, alice, asset(1'000))); + env(pay(issuer, bob, asset(1'000))); + env.close(); + + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(500)})); + env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(500)})); + env.close(); + + auto const shares = [&]() -> PrettyAsset { + auto const sle = env.le(keylet); + BEAST_EXPECT(sle != nullptr); + return MPTIssue(sle->at(sfShareMPTID)); + }(); + + mptt.set({.mutableFlags = tmfMPTClearCanTransfer}); + env.close(); + + // Pre-amendment: share transfer leaks past underlying restriction. + env(pay(alice, bob, shares(1))); + env.close(); + } + + { + testcase("MPT CanTrade governance: share inherits underlying on DEX and AMM"); + + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + env.fund(XRP(100'000), issuer, owner, alice, bob); + env.close(); + Vault const vault{env}; + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create( + {.flags = tfMPTCanTransfer | tfMPTCanTrade | tfMPTCanLock, + .mutableFlags = tmfMPTCanMutateCanTrade}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + mptt.authorize({.account = alice}); + mptt.authorize({.account = bob}); + env(pay(issuer, alice, asset(10'000))); + env(pay(issuer, bob, asset(10'000))); + env.close(); + + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + // Seed shares so we can later place them on trading venues. + env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(5'000)})); + env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(5'000)})); + env.close(); + + auto const shares = [&]() -> PrettyAsset { + auto const sle = env.le(keylet); + BEAST_EXPECT(sle != nullptr); + return MPTIssue(sle->at(sfShareMPTID)); + }(); + + // Sanity: while CanTrade is set on the underlying, both the asset + // and the vault share can be placed on the DEX. + env(offer(alice, XRP(1), asset(10))); + env(offer(alice, XRP(1), shares(1))); + env.close(); + + // Issuer governance: clear CanTrade on the underlying. + mptt.set({.mutableFlags = tmfMPTClearCanTrade}); + env.close(); + + // Control: clearing CanTrade on the underlying is observable on + // the DEX path for that asset. + env(offer(alice, XRP(1), asset(10)), Ter{tecNO_PERMISSION}); + env.close(); + + // Control: clearing CanTrade on the underlying is also observable + // on the AMM path for that asset. + AMM const ammUnderlyingFails( + env, alice, XRP(1'000), asset(1'000), Ter{tecNO_PERMISSION}); + + // Post-fixCleanup3_2_0: vault shares inherit the underlying's + // CanTrade restriction on the DEX path (canTrade reads the + // share's sfReferenceHolding and dispatches to the underlying). + env(offer(bob, XRP(1), shares(1)), Ter{tecNO_PERMISSION}); + env.close(); + + // checkMPTAllowed mirrors the inheritance for AMM/Offer- + // crossing/Check paths, so a share AMM also cannot be created + // when the underlying CanTrade is cleared. + AMM const ammShares(env, alice, XRP(1'000), shares(100), Ter{tecNO_PERMISSION}); + + // Deposit still works (canAddHolding does not consult the field). + env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(100)})); + env.close(); + + // Peer-to-peer share transfers still work (CanTransfer is set on + // both layers). + env(pay(alice, bob, shares(1))); + env.close(); + + // Withdraw still works. + env(vault.withdraw({.depositor = alice, .id = keylet.key, .amount = asset(100)})); + env.close(); + } + { testcase("MPT OutstandingAmount > MaximumAmount"); @@ -2744,16 +2981,17 @@ class Vault_test : public beast::unit_test::Suite env(tx); env.close(); } - env(pay(owner, charlie, shares(100))); - env.close(); - - // Charlie cannot withdraw - auto tx3 = vault.withdraw( - {.depositor = charlie, .id = keylet.key, .amount = shares(100)}); - env(tx3, Ter{terNO_RIPPLE}); - env.close(); - - env(pay(charlie, owner, shares(100))); + // Behavioural shift introduced by share inheritance: + // before fixCleanup3_2_0 this share Payment succeeded + // and the underlying IOU's NoRipple restriction surfaced + // only later on Charlie's withdrawal (terNO_RIPPLE). + // Post-amendment, canTransfer reads the share's + // sfReferenceHolding and dispatches to the underlying IOU; + // rippling is disabled between owner and charlie so the + // share payment itself is now blocked. tecPATH_DRY is + // the path-find layer's translation of the underlying + // terNO_RIPPLE under featureMPTokensV2. + env(pay(owner, charlie, shares(100)), Ter{tecPATH_DRY}); env.close(); } @@ -6140,6 +6378,406 @@ class Vault_test : public beast::unit_test::Suite runTest(amendments); } + void + testReferenceHolding() + { + using namespace test::jtx; + + auto readReferenceHolding = [&](Env const& env, + Keylet const& vaultKeylet) -> std::optional { + auto const sleVault = env.le(vaultKeylet); + if (!sleVault) + return std::nullopt; + auto const sleIssuance = env.le(keylet::mptIssuance(sleVault->at(sfShareMPTID))); + if (!sleIssuance || !sleIssuance->isFieldPresent(sfReferenceHolding)) + return std::nullopt; + return sleIssuance->getFieldH256(sfReferenceHolding); + }; + + // Post-fixCleanup3_2_0: vault share carries sfReferenceHolding + // pointing to the vault pseudo's MPToken (for MPT-backed vaults) + // or RippleState (for IOU-backed vaults). + { + testcase("sfReferenceHolding: MPT-backed vault, post-amendment"); + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + env.fund(XRP(10'000), issuer, owner); + env.close(); + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + auto const sleVault = env.le(keylet); + BEAST_EXPECT(sleVault != nullptr); + auto const pseudoId = sleVault->at(sfAccount); + auto const expected = keylet::mptoken(mptt.issuanceID(), pseudoId).key; + + auto const stored = readReferenceHolding(env, keylet); + BEAST_EXPECT(stored.has_value()); + BEAST_EXPECT(stored && *stored == expected); + // The pointed-to MPToken must actually exist. + BEAST_EXPECT(env.le(keylet::mptoken(mptt.issuanceID(), pseudoId)) != nullptr); + } + + { + testcase("sfReferenceHolding: IOU-backed vault, post-amendment"); + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + env.fund(XRP(10'000), issuer, owner); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const asset = issuer["IOU"]; + env.trust(asset(1'000'000), owner); + env.close(); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + auto const sleVault = env.le(keylet); + BEAST_EXPECT(sleVault != nullptr); + auto const pseudoId = sleVault->at(sfAccount); + auto const expected = keylet::line(pseudoId, asset.raw().get()).key; + + auto const stored = readReferenceHolding(env, keylet); + BEAST_EXPECT(stored.has_value()); + BEAST_EXPECT(stored && *stored == expected); + // The pointed-to RippleState must actually exist. + BEAST_EXPECT(env.le(keylet::line(pseudoId, asset.raw().get())) != nullptr); + } + + // XRP-backed vaults leave the field absent: XRP has no separate + // holding ledger entry and no transferability concept to inherit. + { + testcase("sfReferenceHolding: XRP-backed vault, field absent"); + Env env{*this, testableAmendments()}; + Account const owner{"owner"}; + env.fund(XRP(10'000), owner); + env.close(); + + PrettyAsset const asset{xrpIssue(), 1'000'000}; + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + BEAST_EXPECT(!readReferenceHolding(env, keylet).has_value()); + } + + // Pre-fixCleanup3_2_0: vault share has the field absent regardless + // of underlying type. + { + testcase("sfReferenceHolding: vault share, pre-amendment"); + Env env{*this, testableAmendments() - fixCleanup3_2_0}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + env.fund(XRP(10'000), issuer, owner); + env.close(); + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + BEAST_EXPECT(!readReferenceHolding(env, keylet).has_value()); + } + + // Plain MPTokenIssuanceCreate (not a vault share) must never + // populate the field. Only the post-amendment case is + // interesting; pre-amendment nothing writes the field at all. + { + testcase("sfReferenceHolding: plain MPT issuance never set"); + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + env.fund(XRP(10'000), issuer); + env.close(); + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + env.close(); + + auto const sleIssuance = env.le(keylet::mptIssuance(mptt.issuanceID())); + if (BEAST_EXPECT(sleIssuance)) + BEAST_EXPECT(!sleIssuance->isFieldPresent(sfReferenceHolding)); + } + } + + // Probe every transactor surface that might delete the vault pseudo- + // account's underlying holding (the MPToken or RippleState pointed to + // by sfReferenceHolding). Each scenario asserts either that the + // existing pseudo-account guards stop the deletion at preclaim, or + // that the ledger leaves the holding intact afterwards. This is a + // regression guard: if any of these guards regresses, the share's + // sfReferenceHolding pointer would dangle and the new ValidMPTIssuance + // invariant would catch it - but we want to fail much earlier, at + // the transactor's preclaim / doApply, not at invariant time. + void + testHoldingDeletionBlocked() + { + using namespace test::jtx; + + // Helper: read the share's referenced holding and confirm the + // pointed-to SLE still exists after the probe. + auto referencedHoldingExists = [&](Env const& env, Keylet const& vaultKeylet) -> bool { + auto const sleVault = env.le(vaultKeylet); + if (!sleVault) + return false; + auto const sleIssuance = env.le(keylet::mptIssuance(sleVault->at(sfShareMPTID))); + if (!sleIssuance || !sleIssuance->isFieldPresent(sfReferenceHolding)) + return false; + auto const holdingKey = sleIssuance->getFieldH256(sfReferenceHolding); + return env.le(keylet::unchecked(holdingKey)) != nullptr; + }; + + // ---- MPT-backed vault ---------------------------------------- + { + testcase("vault pseudo MPToken: Clawback blocked by tecPSEUDO_ACCOUNT"); + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + env.fund(XRP(10'000), issuer, owner, depositor); + env.close(); + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + mptt.authorize({.account = depositor}); + env(pay(issuer, depositor, asset(1'000))); + env.close(); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(500)})); + env.close(); + + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + + Account const pseudoAccount{"vault-pseudo", env.le(keylet)->at(sfAccount)}; + // Issuer attempts to claw back the FULL underlying balance + // (500) directly from the vault pseudo-account. With the + // full amount, the doApply path would drain the pseudo's + // MPToken to zero and removeEmptyHolding would erase it - + // if doApply ever ran. SAV's pseudo-account guard at + // Clawback.cpp:201 refuses at preclaim with + // tecPSEUDO_ACCOUNT before any state change. + env(claw(issuer, asset(500), pseudoAccount), Ter{tecPSEUDO_ACCOUNT}); + env.close(); + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + // Sanity: pseudo's full balance is intact. + BEAST_EXPECT(env.balance(pseudoAccount, asset).number() == 500); + } + + { + testcase("vault pseudo MPToken: Issuer cannot Unauthorize pseudo"); + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + env.fund(XRP(10'000), issuer, owner); + env.close(); + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + mptt.authorize({.account = issuer, .holder = owner}); + env.close(); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + + auto const pseudoId = env.le(keylet)->at(sfAccount); + // Issuer attempts MPTokenAuthorize against the pseudo with + // tfMPTUnauthorize. MPTokenAuthorize.cpp blocks pseudo + // accounts via isPseudoAccount; the pseudo's MPToken is + // preserved. Construct the tx manually since the pseudo + // lacks a signing key, and the issuer-driven flavour is + // expressed via sfHolder. + json::Value jv; + jv[sfAccount] = issuer.human(); + jv[sfHolder] = toBase58(pseudoId); + jv[sfMPTokenIssuanceID] = to_string(mptt.issuanceID()); + jv[sfFlags] = tfMPTUnauthorize; + jv[sfTransactionType] = jss::MPTokenAuthorize; + env(jv, Ter{tecNO_PERMISSION}); + env.close(); + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + } + + { + testcase("vault pseudo MPToken: MPTokenIssuanceDestroy blocked while vault holds"); + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + env.fund(XRP(10'000), issuer, owner, depositor); + env.close(); + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + mptt.authorize({.account = depositor}); + env(pay(issuer, depositor, asset(1'000))); + env.close(); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + env(vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(500)})); + env.close(); + + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + + // While the vault holds outstanding underlying, the issuer + // cannot destroy the issuance. tecHAS_OBLIGATIONS confirms + // the protection - and as a side effect, the share's + // sfReferenceHolding pointer cannot be left pointing at a + // ghost issuance. + mptt.destroy({.id = mptt.issuanceID(), .err = tecHAS_OBLIGATIONS}); + env.close(); + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + } + + // ---- IOU-backed vault ---------------------------------------- + { + testcase("vault pseudo trust line: Clawback blocked by tecPSEUDO_ACCOUNT"); + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + env.fund(XRP(10'000), issuer, owner); + env(fset(issuer, asfAllowTrustLineClawback)); + env.close(); + + PrettyAsset const asset = issuer["IOU"]; + env.trust(asset(1'000'000), owner); + env(pay(issuer, owner, asset(1'000))); + env.close(); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(500)})); + env.close(); + + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + + Account const pseudoAccount{"vault-pseudo", env.le(keylet)->at(sfAccount)}; + // Issuer attempts to claw back the FULL IOU balance (500) + // directly from the vault pseudo. With the full amount, the + // doApply path would drain the trust line to zero and (if + // both reserve flags clear) trustDelete would erase it - if + // doApply ever ran. The same SAV pseudo-account guard + // refuses at preclaim with tecPSEUDO_ACCOUNT. The amount's + // STAmount issuer field is the holder, per IOU clawback + // convention. + env(claw(issuer, pseudoAccount["IOU"](500)), Ter{tecPSEUDO_ACCOUNT}); + env.close(); + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + // Sanity: pseudo's full balance is intact. + BEAST_EXPECT(env.balance(pseudoAccount, asset).number() == 500); + } + + { + testcase("vault pseudo trust line: TrustSet limit=0 from issuer preserves line"); + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + env.fund(XRP(10'000), issuer, owner); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const asset = issuer["IOU"]; + env.trust(asset(1'000'000), owner); + env(pay(issuer, owner, asset(1'000))); + env.close(); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + env(vault.deposit({.depositor = owner, .id = keylet.key, .amount = asset(500)})); + env.close(); + + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + + // Issuer submits TrustSet with limit=0 against the vault + // pseudo. The pseudo's side of the line still has the + // original (non-zero) limit and a non-zero balance, so the + // line is preserved - even though the issuer cleared its + // own side. trustDelete only fires when both limits clear + // and the balance is zero. + Account const pseudoAccount{"vault-pseudo", env.le(keylet)->at(sfAccount)}; + env(trust(issuer, pseudoAccount["IOU"](0))); + env.close(); + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + } + + // ---- Positive control: VaultDelete is the only legitimate path + { + testcase("vault pseudo holding: VaultDelete is the legitimate cleanup path"); + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + env.fund(XRP(10'000), issuer, owner); + env.close(); + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); + PrettyAsset const asset = mptt.issuanceID(); + mptt.authorize({.account = owner}); + + Vault const vault{env}; + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + BEAST_EXPECT(referencedHoldingExists(env, keylet)); + auto const pseudoId = env.le(keylet)->at(sfAccount); + auto const sharedMptId = env.le(keylet)->at(sfShareMPTID); + auto const holdingKeylet = keylet::mptoken(mptt.issuanceID(), pseudoId); + + // VaultDelete tears down the vault pseudo's holding, the + // share issuance, and the pseudo-account itself. Invariant + // permits this because the tx is ttVAULT_DELETE. + env(vault.del({.owner = owner, .id = keylet.key})); + env.close(); + + BEAST_EXPECT(env.le(keylet) == nullptr); + BEAST_EXPECT(env.le(holdingKeylet) == nullptr); + BEAST_EXPECT(env.le(keylet::mptIssuance(sharedMptId)) == nullptr); + } + } + // VaultDeposit::preclaim uses accountHolds(..., SpendableHandling:: // shFULL_BALANCE), which for an IOU asset adds the counterparty's // LowLimit/HighLimit to the depositor's raw balance (TokenHelpers.cpp: @@ -6243,6 +6881,8 @@ public: testAssetsMaximum(); testBug6LimitBypassWithShares(); testRemoveEmptyHoldingLockedAmount(); + testReferenceHolding(); + testHoldingDeletionBlocked(); } }; diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp index e74af94a5f..7479f0c63c 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp @@ -33,6 +33,7 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) auto const previousTxnLgrSeqValue = canonical_UINT32(); auto const domainIDValue = canonical_UINT256(); auto const mutableFlagsValue = canonical_UINT32(); + auto const referenceHoldingValue = canonical_UINT256(); MPTokenIssuanceBuilder builder{ issuerValue, @@ -50,6 +51,7 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) builder.setMPTokenMetadata(mPTokenMetadataValue); builder.setDomainID(domainIDValue); builder.setMutableFlags(mutableFlagsValue); + builder.setReferenceHolding(referenceHoldingValue); builder.setLedgerIndex(index); builder.setFlags(0x1u); @@ -152,6 +154,14 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) EXPECT_TRUE(entry.hasMutableFlags()); } + { + auto const& expected = referenceHoldingValue; + auto const actualOpt = entry.getReferenceHolding(); + ASSERT_TRUE(actualOpt.has_value()); + expectEqualField(expected, *actualOpt, "sfReferenceHolding"); + EXPECT_TRUE(entry.hasReferenceHolding()); + } + EXPECT_TRUE(entry.hasLedgerIndex()); auto const ledgerIndex = entry.getLedgerIndex(); ASSERT_TRUE(ledgerIndex.has_value()); @@ -178,6 +188,7 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) auto const previousTxnLgrSeqValue = canonical_UINT32(); auto const domainIDValue = canonical_UINT256(); auto const mutableFlagsValue = canonical_UINT32(); + auto const referenceHoldingValue = canonical_UINT256(); auto sle = std::make_shared(MPTokenIssuance::entryType, index); @@ -194,6 +205,7 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; sle->at(sfDomainID) = domainIDValue; sle->at(sfMutableFlags) = mutableFlagsValue; + sle->at(sfReferenceHolding) = referenceHoldingValue; MPTokenIssuanceBuilder builderFromSle{sle}; EXPECT_TRUE(builderFromSle.validate()); @@ -355,6 +367,19 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) expectEqualField(expected, *fromBuilderOpt, "sfMutableFlags"); } + { + auto const& expected = referenceHoldingValue; + + auto const fromSleOpt = entryFromSle.getReferenceHolding(); + auto const fromBuilderOpt = entryFromBuilder.getReferenceHolding(); + + ASSERT_TRUE(fromSleOpt.has_value()); + ASSERT_TRUE(fromBuilderOpt.has_value()); + + expectEqualField(expected, *fromSleOpt, "sfReferenceHolding"); + expectEqualField(expected, *fromBuilderOpt, "sfReferenceHolding"); + } + EXPECT_EQ(entryFromSle.getKey(), index); EXPECT_EQ(entryFromBuilder.getKey(), index); } @@ -433,5 +458,7 @@ TEST(MPTokenIssuanceTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(entry.getDomainID().has_value()); EXPECT_FALSE(entry.hasMutableFlags()); EXPECT_FALSE(entry.getMutableFlags().has_value()); + EXPECT_FALSE(entry.hasReferenceHolding()); + EXPECT_FALSE(entry.getReferenceHolding().has_value()); } } From a5d238e7d4fa6ef2b539b759d58744d0a1c33c0c Mon Sep 17 00:00:00 2001 From: box4wangjing Date: Thu, 21 May 2026 04:46:45 +0900 Subject: [PATCH 02/87] docs: Fix some comments to improve readability (#7122) Signed-off-by: box4wangjing Co-authored-by: Mayukha Vadari --- include/xrpl/json/Writer.h | 2 +- include/xrpl/protocol/detail/ledger_entries.macro | 2 +- include/xrpl/shamap/README.md | 2 +- src/libxrpl/protocol/STAmount.cpp | 2 +- src/test/app/NFTokenBurn_test.cpp | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/xrpl/json/Writer.h b/include/xrpl/json/Writer.h index 664a2a75c1..87e3e99c7e 100644 --- a/include/xrpl/json/Writer.h +++ b/include/xrpl/json/Writer.h @@ -161,7 +161,7 @@ public: * While the JSON spec doesn't explicitly disallow this, you should avoid * calling this method twice with the same tag for the same object. * - * If CHECK_JSON_WRITER is defined, this function throws an exception if if + * If CHECK_JSON_WRITER is defined, this function throws an exception if * the tag you use has already been used in this object. */ template diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 19b8b44fe6..632038a9c5 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -592,7 +592,7 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({ // LoanBroker.ManagementFeeRate // The unrounded true total fee still owed to the broker. // - // Note the the "True" values may differ significantly from the tracked + // Note the "True" values may differ significantly from the tracked // rounded values. {sfPaymentRemaining, SoeDefault}, {sfPeriodicPayment, SoeRequired}, diff --git a/include/xrpl/shamap/README.md b/include/xrpl/shamap/README.md index ae37d23ac3..8931a1b50c 100644 --- a/include/xrpl/shamap/README.md +++ b/include/xrpl/shamap/README.md @@ -231,7 +231,7 @@ The `fetchNodeNT()` method goes through three phases: will be 0. 2. If the node is not in the TreeNodeCache, we attempt to locate the node - in the historic data stored by the data base. The call to to + in the historic data stored by the data base. The call to `fetchNodeFromDB(hash)` does that work for us. 3. Finally if a filter exists, we check if it can supply the node. This is diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 3cbf03575b..2282488fb6 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -1505,7 +1505,7 @@ roundToScale(STAmount const& value, std::int32_t scale, Number::RoundingMode rou STAmount const referenceValue{value.asset(), STAmount::kMinValue, scale, value.negative()}; NumberRoundModeGuard const mg(rounding); - // With an IOU, the the result of addition will be truncated to the + // With an IOU, the result of addition will be truncated to the // precision of the larger value, which in this case is referenceValue. Then // remove the reference value via subtraction, and we're left with the // rounded value. diff --git a/src/test/app/NFTokenBurn_test.cpp b/src/test/app/NFTokenBurn_test.cpp index f08793da72..482d275d51 100644 --- a/src/test/app/NFTokenBurn_test.cpp +++ b/src/test/app/NFTokenBurn_test.cpp @@ -515,7 +515,7 @@ class NFTokenBurn_test : public beast::unit_test::Suite { // Removing the last token from the last page deletes the // _previous_ page because we need to preserve that last - // page an an anchor. The contents of the next-to-last page + // page as an anchor. The contents of the next-to-last page // are moved into the last page. lastNFTokenPage = env.le(keylet::nftpageMax(alice)); BEAST_EXPECT(lastNFTokenPage); @@ -694,7 +694,7 @@ class NFTokenBurn_test : public beast::unit_test::Suite { // Removing the last token from the last page deletes the // _previous_ page because we need to preserve that last - // page an an anchor. The contents of the next-to-last page + // page as an anchor. The contents of the next-to-last page // are moved into the last page. lastNFTokenPage = env.le(keylet::nftpageMax(alice)); BEAST_EXPECT(lastNFTokenPage); From 242ce3e9e4280aa7f9e6292062117c8ccd8861c8 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 20 May 2026 15:47:59 -0400 Subject: [PATCH 03/87] refactor: Fix `sfGeneric` and `sfInvalid` field names (#7300) --- include/xrpl/protocol/SField.h | 4 +-- include/xrpl/protocol/STBlob.h | 2 +- src/libxrpl/protocol/SField.cpp | 8 ++--- src/libxrpl/protocol/STAmount.cpp | 2 +- src/libxrpl/protocol/STBase.cpp | 2 +- src/libxrpl/protocol/STParsedJSON.cpp | 12 +++---- src/libxrpl/protocol/XChainAttestations.cpp | 4 +-- src/libxrpl/server/Manifest.cpp | 10 +++--- src/test/app/Manifest_test.cpp | 18 +++++------ src/test/app/Path_test.cpp | 6 ++-- src/test/app/ValidatorList_test.cpp | 4 +-- src/test/jtx/TrustedPublisherServer.h | 2 +- src/test/jtx/impl/TestHelpers.cpp | 6 ++-- src/test/overlay/compression_test.cpp | 6 ++-- src/test/protocol/Hooks_test.cpp | 6 ++-- src/test/protocol/STAmount_test.cpp | 2 +- src/test/protocol/STObject_test.cpp | 32 +++++++++---------- src/test/protocol/STTx_test.cpp | 2 +- src/test/rpc/Simulate_test.cpp | 4 +-- .../libxrpl/protocol_autogen/TestHelpers.h | 2 +- .../rpc/handlers/transaction/Simulate.cpp | 2 +- 21 files changed, 68 insertions(+), 68 deletions(-) diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index 1d4e4e69a0..34fb66ce00 100644 --- a/include/xrpl/protocol/SField.h +++ b/include/xrpl/protocol/SField.h @@ -365,8 +365,8 @@ using SF_XCHAIN_BRIDGE = TypedField; #define UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) extern SField const sfName; #define TYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) extern SF_##stiSuffix const sfName; -extern SField const kSfInvalid; -extern SField const kSfGeneric; +extern SField const sfInvalid; // NOLINT(readability-identifier-naming) +extern SField const sfGeneric; // NOLINT(readability-identifier-naming) #include diff --git a/include/xrpl/protocol/STBlob.h b/include/xrpl/protocol/STBlob.h index c9110a367e..0667c54e30 100644 --- a/include/xrpl/protocol/STBlob.h +++ b/include/xrpl/protocol/STBlob.h @@ -24,7 +24,7 @@ public: STBlob(SField const& f, void const* data, std::size_t size); STBlob(SField const& f, Buffer&& b); STBlob(SField const& n); - STBlob(SerialIter&, SField const& name = kSfGeneric); + STBlob(SerialIter&, SField const& name = sfGeneric); [[nodiscard]] std::size_t size() const; diff --git a/src/libxrpl/protocol/SField.cpp b/src/libxrpl/protocol/SField.cpp index f2a523db49..a2d4903c6a 100644 --- a/src/libxrpl/protocol/SField.cpp +++ b/src/libxrpl/protocol/SField.cpp @@ -53,8 +53,8 @@ TypedField::TypedField(PrivateAccessTagT pat, Args&&... args) ##__VA_ARGS__); // SFields which, for historical reasons, do not follow naming conventions. -SField const kSfInvalid(access, -1, ""); -SField const kSfGeneric(access, 0, "Generic"); +SField const sfInvalid(access, -1, ""); +SField const sfGeneric(access, 0, "Generic"); // The following two fields aren't used anywhere, but they break tests/have // downstream effects. SField const kSfHash(access, STI_UINT256, 257, "hash"); @@ -121,7 +121,7 @@ SField::getField(int code) { return *(it->second); } - return kSfInvalid; + return sfInvalid; } int @@ -149,7 +149,7 @@ SField::getField(std::string const& fieldName) { return *(it->second); } - return kSfInvalid; + return sfInvalid; } } // namespace xrpl diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 2282488fb6..2d051722bf 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -1151,7 +1151,7 @@ amountFromJsonNoThrow(STAmount& result, json::Value const& jvSource) { try { - result = amountFromJson(kSfGeneric, jvSource); + result = amountFromJson(sfGeneric, jvSource); return true; } catch (std::exception const& e) diff --git a/src/libxrpl/protocol/STBase.cpp b/src/libxrpl/protocol/STBase.cpp index d1ae1e5aa8..f029f10e75 100644 --- a/src/libxrpl/protocol/STBase.cpp +++ b/src/libxrpl/protocol/STBase.cpp @@ -12,7 +12,7 @@ namespace xrpl { -STBase::STBase() : fName_(&kSfGeneric) +STBase::STBase() : fName_(&sfGeneric) { } diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp index 7b9989afac..64c4dfd1be 100644 --- a/src/libxrpl/protocol/STParsedJSON.cpp +++ b/src/libxrpl/protocol/STParsedJSON.cpp @@ -258,7 +258,7 @@ parseUInt16( safeCast(static_cast( TxFormats::getInstance().findTypeByName(strValue)))); - if (*name == kSfGeneric) + if (*name == sfGeneric) name = &sfTransaction; } else if (field == sfLedgerEntryType) @@ -268,7 +268,7 @@ parseUInt16( safeCast(static_cast( LedgerFormats::getInstance().findTypeByName(strValue)))); - if (*name == kSfGeneric) + if (*name == sfGeneric) name = &sfLedgerEntry; } else @@ -361,7 +361,7 @@ parseLeaf( auto const& field = SField::getField(fieldName); // checked in parseObject - if (field == kSfInvalid) + if (field == sfInvalid) { // LCOV_EXCL_START error = unknownField(jsonName, fieldName); @@ -1013,7 +1013,7 @@ parseObject( json::Value const& value = json[fieldName]; auto const& field = SField::getField(fieldName); - if (field == kSfInvalid) + if (field == sfInvalid) { error = unknownField(jsonName, fieldName); return std::nullopt; @@ -1144,7 +1144,7 @@ parseArray( std::string const memberName(json[i].getMemberNames()[0]); auto const& nameField(SField::getField(memberName)); - if (nameField == kSfInvalid) + if (nameField == sfInvalid) { error = unknownField(jsonName, memberName); return std::nullopt; @@ -1189,7 +1189,7 @@ parseArray( STParsedJSONObject::STParsedJSONObject(std::string const& name, json::Value const& json) { using namespace STParsedJSONDetail; - object = parseObject(name, json, kSfGeneric, 0, error); + object = parseObject(name, json, sfGeneric, 0, error); } } // namespace xrpl diff --git a/src/libxrpl/protocol/XChainAttestations.cpp b/src/libxrpl/protocol/XChainAttestations.cpp index e41e5f3d61..805d08c097 100644 --- a/src/libxrpl/protocol/XChainAttestations.cpp +++ b/src/libxrpl/protocol/XChainAttestations.cpp @@ -195,7 +195,7 @@ AttestationClaim::message( std::uint64_t claimID, std::optional const& dst) { - STObject o{kSfGeneric}; + STObject o{sfGeneric}; // Serialize in SField order to make python serializers easier to write o[sfXChainClaimID] = claimID; o[sfAmount] = sendingAmount; @@ -332,7 +332,7 @@ AttestationCreateAccount::message( std::uint64_t createCount, AccountID const& dst) { - STObject o{kSfGeneric}; + STObject o{sfGeneric}; // Serialize in SField order to make python serializers easier to write o[sfXChainAccountCreateCount] = createCount; o[sfAmount] = sendingAmount; diff --git a/src/libxrpl/server/Manifest.cpp b/src/libxrpl/server/Manifest.cpp index 1b0493dd9d..b26c67e531 100644 --- a/src/libxrpl/server/Manifest.cpp +++ b/src/libxrpl/server/Manifest.cpp @@ -90,7 +90,7 @@ deserializeManifest(Slice s, beast::Journal journal) try { SerialIter sit{s}; - STObject st{sit, kSfGeneric}; + STObject st{sit, sfGeneric}; st.applyTemplate(kManifestFormat); @@ -193,7 +193,7 @@ logMftAct( bool Manifest::verify() const { - STObject st(kSfGeneric); + STObject st(sfGeneric); SerialIter sit(serialized.data(), serialized.size()); st.set(sit); @@ -213,7 +213,7 @@ Manifest::verify() const uint256 Manifest::hash() const { - STObject st(kSfGeneric); + STObject st(sfGeneric); SerialIter sit(serialized.data(), serialized.size()); st.set(sit); return st.getHash(HashPrefix::Manifest); @@ -240,7 +240,7 @@ Manifest::revoked(std::uint32_t sequence) std::optional Manifest::getSignature() const { - STObject st(kSfGeneric); + STObject st(sfGeneric); SerialIter sit(serialized.data(), serialized.size()); st.set(sit); if (!get(st, sfSignature)) @@ -251,7 +251,7 @@ Manifest::getSignature() const Blob Manifest::getMasterSignature() const { - STObject st(kSfGeneric); + STObject st(sfGeneric); SerialIter sit(serialized.data(), serialized.size()); st.set(sit); return st.getFieldVL(sfMasterSignature); diff --git a/src/test/app/Manifest_test.cpp b/src/test/app/Manifest_test.cpp index 55aa93bf79..d559ecd7b5 100644 --- a/src/test/app/Manifest_test.cpp +++ b/src/test/app/Manifest_test.cpp @@ -115,7 +115,7 @@ public: SecretKey const& ssk, int seq) { - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = seq; st[sfPublicKey] = pk; st[sfSigningPubKey] = spk; @@ -137,7 +137,7 @@ public: { auto const pk = derivePublicKey(type, sk); - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = std::numeric_limits::max(); st[sfPublicKey] = pk; @@ -156,7 +156,7 @@ public: { auto const pk = derivePublicKey(type, sk); - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = std::numeric_limits::max(); st[sfPublicKey] = pk; @@ -186,7 +186,7 @@ public: auto const pk = derivePublicKey(type, sk); auto const spk = derivePublicKey(stype, ssk); - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = seq; st[sfPublicKey] = pk; st[sfSigningPubKey] = spk; @@ -363,7 +363,7 @@ public: auto const kp = randomKeyPair(KeyType::Secp256k1); auto const m = makeManifest(sk, KeyType::Ed25519, kp.second, KeyType::Secp256k1, 0); - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = 0; st[sfPublicKey] = pk; st[sfSigningPubKey] = kp.first; @@ -495,7 +495,7 @@ public: auto const spk = derivePublicKey(KeyType::Secp256k1, ssk); auto buildManifestObject = [&](std::uint16_t version) { - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = 3; st[sfPublicKey] = pk; st[sfSigningPubKey] = spk; @@ -573,7 +573,7 @@ public: std::optional domain, bool noSigningPublic = false, bool noSignature = false) { - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = seq; st[sfPublicKey] = pk; @@ -724,7 +724,7 @@ public: } { // reject matching master & ephemeral keys - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = 314159; st[sfPublicKey] = pk; st[sfSigningPubKey] = pk; @@ -797,7 +797,7 @@ public: auto const pk2 = derivePublicKey(KeyType::Secp256k1, sk2); auto test = [&](std::string domain) { - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = 7; st[sfPublicKey] = pk1; st[sfDomain] = makeSlice(domain); diff --git a/src/test/app/Path_test.cpp b/src/test/app/Path_test.cpp index 22044a2572..1cf8131206 100644 --- a/src/test/app/Path_test.cpp +++ b/src/test/app/Path_test.cpp @@ -216,7 +216,7 @@ public: STAmount da; if (result.isMember(jss::destination_amount)) - da = amountFromJson(kSfGeneric, result[jss::destination_amount]); + da = amountFromJson(sfGeneric, result[jss::destination_amount]); STAmount sa; STPathSet paths; @@ -228,10 +228,10 @@ public: auto const& path = alts[0u]; if (path.isMember(jss::source_amount)) - sa = amountFromJson(kSfGeneric, path[jss::source_amount]); + sa = amountFromJson(sfGeneric, path[jss::source_amount]); if (path.isMember(jss::destination_amount)) - da = amountFromJson(kSfGeneric, path[jss::destination_amount]); + da = amountFromJson(sfGeneric, path[jss::destination_amount]); if (path.isMember(jss::paths_computed)) { diff --git a/src/test/app/ValidatorList_test.cpp b/src/test/app/ValidatorList_test.cpp index 15bd1bc586..1b66dd305d 100644 --- a/src/test/app/ValidatorList_test.cpp +++ b/src/test/app/ValidatorList_test.cpp @@ -81,7 +81,7 @@ private: SecretKey const& ssk, int seq) { - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = seq; st[sfPublicKey] = pk; @@ -104,7 +104,7 @@ private: static std::string makeRevocationString(PublicKey const& pk, SecretKey const& sk) { - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = std::numeric_limits::max(); st[sfPublicKey] = pk; diff --git a/src/test/jtx/TrustedPublisherServer.h b/src/test/jtx/TrustedPublisherServer.h index 4c7528ccc3..9a53a32481 100644 --- a/src/test/jtx/TrustedPublisherServer.h +++ b/src/test/jtx/TrustedPublisherServer.h @@ -101,7 +101,7 @@ public: SecretKey const& ssk, int seq) { - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = seq; st[sfPublicKey] = pk; st[sfSigningPubKey] = spk; diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index 3e65410825..e9ea48fb39 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -262,7 +262,7 @@ findPaths( STAmount da; if (result.isMember(jss::destination_amount)) - da = amountFromJson(kSfGeneric, result[jss::destination_amount]); + da = amountFromJson(sfGeneric, result[jss::destination_amount]); STAmount sa; STPathSet paths; @@ -274,10 +274,10 @@ findPaths( auto const& path = alts[0u]; if (path.isMember(jss::source_amount)) - sa = amountFromJson(kSfGeneric, path[jss::source_amount]); + sa = amountFromJson(sfGeneric, path[jss::source_amount]); if (path.isMember(jss::destination_amount)) - da = amountFromJson(kSfGeneric, path[jss::destination_amount]); + da = amountFromJson(sfGeneric, path[jss::destination_amount]); if (path.isMember(jss::paths_computed)) { diff --git a/src/test/overlay/compression_test.cpp b/src/test/overlay/compression_test.cpp index adb3b1b27b..d98ffa9b33 100644 --- a/src/test/overlay/compression_test.cpp +++ b/src/test/overlay/compression_test.cpp @@ -142,7 +142,7 @@ public: { auto master = randomKeyPair(KeyType::Ed25519); auto signing = randomKeyPair(KeyType::Ed25519); - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = i; st[sfPublicKey] = std::get<0>(master); st[sfSigningPubKey] = std::get<0>(signing); @@ -299,7 +299,7 @@ public: auto master = randomKeyPair(KeyType::Ed25519); auto signing = randomKeyPair(KeyType::Ed25519); - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = 0; st[sfPublicKey] = std::get<0>(master); st[sfSigningPubKey] = std::get<0>(signing); @@ -326,7 +326,7 @@ public: auto master = randomKeyPair(KeyType::Ed25519); auto signing = randomKeyPair(KeyType::Ed25519); - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfSequence] = 0; st[sfPublicKey] = std::get<0>(master); st[sfSigningPubKey] = std::get<0>(signing); diff --git a/src/test/protocol/Hooks_test.cpp b/src/test/protocol/Hooks_test.cpp index 20fe205579..082507aca7 100644 --- a/src/test/protocol/Hooks_test.cpp +++ b/src/test/protocol/Hooks_test.cpp @@ -73,7 +73,7 @@ class Hooks_test : public beast::unit_test::Suite { SField const& f = rf.get(); - STObject dummy{kSfGeneric}; + STObject dummy{sfGeneric}; BEAST_EXPECT(!dummy.isFieldPresent(f)); @@ -161,8 +161,8 @@ class Hooks_test : public beast::unit_test::Suite case STI_ARRAY: { STArray dummy2{f, 2}; - dummy2.pushBack(STObject{kSfGeneric}); - dummy2.pushBack(STObject{kSfGeneric}); + dummy2.pushBack(STObject{sfGeneric}); + dummy2.pushBack(STObject{sfGeneric}); dummy.setFieldArray(f, dummy2); BEAST_EXPECT(dummy.getFieldArray(f) == dummy2); BEAST_EXPECT(dummy.isFieldPresent(f)); diff --git a/src/test/protocol/STAmount_test.cpp b/src/test/protocol/STAmount_test.cpp index b387c361cb..322d51f84d 100644 --- a/src/test/protocol/STAmount_test.cpp +++ b/src/test/protocol/STAmount_test.cpp @@ -37,7 +37,7 @@ public: s.add(ser); SerialIter sit(ser.slice()); - return STAmount(sit, kSfGeneric); + return STAmount(sit, sfGeneric); } //-------------------------------------------------------------------------- diff --git a/src/test/protocol/STObject_test.cpp b/src/test/protocol/STObject_test.cpp index 59b78d213d..801eebbe63 100644 --- a/src/test/protocol/STObject_test.cpp +++ b/src/test/protocol/STObject_test.cpp @@ -36,19 +36,19 @@ public: { testcase("serialization"); - unexpected(kSfGeneric.isUseful(), "sfGeneric must not be useful"); + unexpected(sfGeneric.isUseful(), "sfGeneric must not be useful"); { // Try to put sfGeneric in an SOTemplate. except( - [&]() { SOTemplate const elements{{kSfGeneric, SoeRequired}}; }); + [&]() { SOTemplate const elements{{sfGeneric, SoeRequired}}; }); } - unexpected(kSfInvalid.isUseful(), "sfInvalid must not be useful"); + unexpected(sfInvalid.isUseful(), "sfInvalid must not be useful"); { // Test return of sfInvalid. auto testInvalid = [this](SerializedTypeID tid, int fv) { SField const& shouldBeInvalid{SField::getField(tid, fv)}; - BEAST_EXPECT(shouldBeInvalid == kSfInvalid); + BEAST_EXPECT(shouldBeInvalid == sfInvalid); }; testInvalid(STI_VL, 255); testInvalid(STI_UINT256, 255); @@ -59,7 +59,7 @@ public: { // Try to put sfInvalid in an SOTemplate. except( - [&]() { SOTemplate const elements{{kSfInvalid, SoeRequired}}; }); + [&]() { SOTemplate const elements{{sfInvalid, SoeRequired}}; }); } { // Try to put the same SField into an SOTemplate twice. @@ -188,7 +188,7 @@ public: { auto const st = [&]() { - STObject s(kSfGeneric); + STObject s(sfGeneric); s.setFieldU32(sf1Outer, 1); s.setFieldU32(sf2Outer, 2); return s; @@ -219,7 +219,7 @@ public: { auto const st = [&]() { - STObject s(sotOuter, kSfGeneric); + STObject s(sotOuter, sfGeneric); s.setFieldU32(sf1Outer, 1); s.setFieldU32(sf2Outer, 2); return s; @@ -239,7 +239,7 @@ public: // write free object { - STObject st(kSfGeneric); + STObject st(sfGeneric); unexcept([&]() { st[sf1Outer]; }); except([&]() { return st[sf1Outer] == 0; }); BEAST_EXPECT(st[~sf1Outer] == std::nullopt); @@ -295,7 +295,7 @@ public: // Write templated object { - STObject st(sotOuter, kSfGeneric); + STObject st(sotOuter, sfGeneric); BEAST_EXPECT(!!st[~sf1Outer]); BEAST_EXPECT(st[~sf1Outer] != std::nullopt); BEAST_EXPECT(st[sf1Outer] == 0); @@ -353,7 +353,7 @@ public: // coercion operator to std::optional { - STObject st(kSfGeneric); + STObject st(sfGeneric); auto const v = ~st[~sf1Outer]; static_assert( std::is_same_v, std::optional>, ""); @@ -362,7 +362,7 @@ public: // UDT scalar fields { - STObject st(kSfGeneric); + STObject st(sfGeneric); st[sfAmount] = STAmount{}; st[sfAccount] = AccountID{}; st[sfDigest] = uint256{}; @@ -375,7 +375,7 @@ public: { { - STObject st(kSfGeneric); + STObject st(sfGeneric); Buffer b(1); BEAST_EXPECT(!b.empty()); st[sf4] = std::move(b); @@ -392,7 +392,7 @@ public: BEAST_EXPECT(Slice(st[sf5]).size() == 2); } { - STObject st(sotOuter, kSfGeneric); + STObject st(sotOuter, sfGeneric); BEAST_EXPECT(st[sf5] == Slice{}); BEAST_EXPECT(!!st[~sf5]); BEAST_EXPECT(!!~st[~sf5]); @@ -408,7 +408,7 @@ public: // UDT blobs { - STObject st(kSfGeneric); + STObject st(sfGeneric); BEAST_EXPECT(!st[~sf5]); auto const kp = generateKeyPair(KeyType::Secp256k1, generateSeed("masterpassphrase")); st[sf5] = kp.first; @@ -419,7 +419,7 @@ public: { auto const& sf = sfIndexes; - STObject st(kSfGeneric); + STObject st(sfGeneric); std::vector v; v.emplace_back(1); v.emplace_back(2); @@ -446,7 +446,7 @@ public: {sf3, SoeDefault}, }; - STObject st(sot, kSfGeneric); + STObject st(sot, sfGeneric); auto const& cst(st); BEAST_EXPECT(cst[sf1].empty()); BEAST_EXPECT(!cst[~sf2]); diff --git a/src/test/protocol/STTx_test.cpp b/src/test/protocol/STTx_test.cpp index 42cb2bb9a7..21518d1406 100644 --- a/src/test/protocol/STTx_test.cpp +++ b/src/test/protocol/STTx_test.cpp @@ -1390,7 +1390,7 @@ public: // Lambda that returns a Payment STObject. auto getPayment = [kp1, id1, id2]() { // Account id1 pays account id2 10,000 XRP. - STObject payment(kSfGeneric); + STObject payment(sfGeneric); payment.setFieldU16(sfTransactionType, ttPAYMENT); payment.setAccountID(sfAccount, id1); payment.setAccountID(sfDestination, id2); diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index b66b3dfe9f..60452e386a 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -66,7 +66,7 @@ class Simulate_test : public beast::unit_test::Suite { auto const unHexed = strUnHex(result[jss::tx_blob].asString()); SerialIter sitTrans(makeSlice(*unHexed)); // NOLINT(bugprone-unchecked-optional-access) - txJson = STObject(std::ref(sitTrans), kSfGeneric).getJson(JsonOptions::Values::None); + txJson = STObject(std::ref(sitTrans), sfGeneric).getJson(JsonOptions::Values::None); } BEAST_EXPECT(txJson[jss::TransactionType] == tx[jss::TransactionType]); BEAST_EXPECT(txJson[jss::Account] == tx[jss::Account]); @@ -162,7 +162,7 @@ class Simulate_test : public beast::unit_test::Suite { auto unHexed = strUnHex(txResult[jss::meta_blob].asString()); SerialIter sitTrans(makeSlice(*unHexed)); // NOLINT(bugprone-unchecked-optional-access) - return STObject(std::ref(sitTrans), kSfGeneric).getJson(JsonOptions::Values::None); + return STObject(std::ref(sitTrans), sfGeneric).getJson(JsonOptions::Values::None); } return txResult[jss::meta]; diff --git a/src/tests/libxrpl/protocol_autogen/TestHelpers.h b/src/tests/libxrpl/protocol_autogen/TestHelpers.h index b8449f2bca..a32ab5e20c 100644 --- a/src/tests/libxrpl/protocol_autogen/TestHelpers.h +++ b/src/tests/libxrpl/protocol_autogen/TestHelpers.h @@ -159,7 +159,7 @@ canonical_ARRAY() inline STObject canonical_OBJECT() { - return STObject{kSfGeneric}; + return STObject{sfGeneric}; } inline STPathSet diff --git a/src/xrpld/rpc/handlers/transaction/Simulate.cpp b/src/xrpld/rpc/handlers/transaction/Simulate.cpp index ba108eb5b2..7a11b728ce 100644 --- a/src/xrpld/rpc/handlers/transaction/Simulate.cpp +++ b/src/xrpld/rpc/handlers/transaction/Simulate.cpp @@ -194,7 +194,7 @@ getTxJsonFromParams(json::Value const& params) try { SerialIter sitTrans(makeSlice(*unHexed)); - txJson = STObject(std::ref(sitTrans), kSfGeneric).getJson(JsonOptions::Values::None); + txJson = STObject(std::ref(sitTrans), sfGeneric).getJson(JsonOptions::Values::None); } catch (std::runtime_error const&) { From 9cb07406735a6bb967015495c33668d904d9ddee Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Wed, 20 May 2026 16:24:09 -0400 Subject: [PATCH 04/87] fix: Fix multisign and signfor to check for delegate (#7064) --- src/libxrpl/protocol/STTx.cpp | 4 +- src/test/app/Delegate_test.cpp | 146 +++++++++++++++++++++++ src/xrpld/rpc/detail/TransactionSign.cpp | 8 +- 3 files changed, 155 insertions(+), 3 deletions(-) diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 878aa296dd..8636c99284 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -535,8 +535,10 @@ STTx::checkMultiSign(Rules const& rules, STObject const& sigObject) const { // Used inside the loop in multiSignHelper to enforce that // the account owner may not multisign for themselves. + // 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(getAccountID(sfAccount)); + &sigObject != this ? std::nullopt : std::optional(getFeePayer()); // We can ease the computational load inside the loop a bit by // pre-constructing part of the data that we hash. Fill a Serializer diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 06884e9931..588aeee634 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1879,6 +1880,149 @@ class Delegate_test : public beast::unit_test::Suite BEAST_EXPECT(env.balance(edward) == edwardBalance); } + void + testMultiSignDelegatorAsSigner() + { + // checkMultiSign disallows the owner of the account to + // be part of the multisigner list. When it is a delegated transaction, + // the delegate account should not be part of the multisigner list. + testcase("test delegator as multisigner in delegate's signer list"); + using namespace jtx; + + Env env(*this); + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; + Account const daria{"daria"}; + env.fund(XRP(100000), alice, bob, carol, daria); + env.close(); + + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + // bob's signer list includes the delegator alice and daria + env(signers(bob, 2, {{alice, 1}, {daria, 1}})); + env.close(); + + auto aliceBalance = env.balance(alice); + auto bobBalance = env.balance(bob); + auto carolBalance = env.balance(carol); + auto const amt = 100; + + // alice can sign as a multisigner for bob + env(pay(alice, carol, XRP(100)), Fee(XRP(10)), delegate::As(bob), Msig(alice, daria)); + env.close(); + + BEAST_EXPECT(env.balance(alice) == aliceBalance - XRP(amt)); + BEAST_EXPECT(env.balance(bob) == bobBalance - XRP(10)); + BEAST_EXPECT(env.balance(carol) == carolBalance + XRP(amt)); + + // alice can not sign as a multisigner if she sent the transaction by herself. + env(pay(alice, carol, XRP(100)), Fee(XRP(10)), Msig(alice, daria), Ter(telENV_RPC_FAILED)); + env.close(); + + // Get new balances + aliceBalance = env.balance(alice); + bobBalance = env.balance(bob); + carolBalance = env.balance(carol); + + // bob (the delegate) should not appear as a multisigner in his transaction sent on behalf + // of alice. STTx::checkMultiSign catches this at the local-check stage, so the jtx + // framework returns telENV_RPC_FAILED. + env(pay(alice, carol, XRP(50)), + Fee(XRP(10)), + delegate::As(bob), + Msig(alice, bob), + Ter(telENV_RPC_FAILED)); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance); + BEAST_EXPECT(env.balance(bob) == bobBalance); + BEAST_EXPECT(env.balance(carol) == carolBalance); + } + + void + testSignForDelegated() + { + // In sortAndValidateSigners, if it is a delegated transaction, the delegate account is + // the forbidden account from appearing in its own Signers array. + testcase("test sign_for with delegated transaction"); + using namespace jtx; + + Env env(*this); + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; + Account const daria{"daria"}; + env.fund(XRP(100000), alice, bob, carol, daria); + env.close(); + + env(delegate::set(alice, bob, {"Payment"})); + env.close(); + + // bob's signer list includes the delegator alice and daria + env(signers(bob, 2, {{alice, 1}, {daria, 1}})); + env.close(); + + auto const baseFee = env.current()->fees().base; + + auto const sendAmt = 1'000'000; + auto makeDelegateTx = [&]() -> json::Value { + json::Value jv; + jv[jss::tx_json][jss::Account] = alice.human(); + jv[jss::tx_json][sfDelegate.jsonName] = bob.human(); + jv[jss::tx_json][jss::TransactionType] = jss::Payment; + jv[jss::tx_json][jss::Destination] = carol.human(); + jv[jss::tx_json][jss::Amount] = sendAmt; + jv[jss::tx_json][jss::Fee] = std::to_string((10 * baseFee).drops()); + jv[jss::tx_json][jss::Sequence] = env.seq(alice); + jv[jss::tx_json][jss::SigningPubKey] = ""; + return jv; + }; + + // The delegator alice and daria both sign via sign_for, which is valid + { + auto const aliceBalance = env.balance(alice); + auto const bobBalance = env.balance(bob); + auto const dariaBalance = env.balance(daria); + auto const carolBalance = env.balance(carol); + + json::Value jv = makeDelegateTx(); + jv[jss::account] = alice.human(); + jv[jss::secret] = alice.name(); + auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result]; + BEAST_EXPECT(jrr[jss::status] == "success"); + + json::Value jv2; + jv2[jss::tx_json] = jrr[jss::tx_json]; + jv2[jss::account] = daria.human(); + jv2[jss::secret] = daria.name(); + jrr = env.rpc("json", "sign_for", to_string(jv2))[jss::result]; + BEAST_EXPECT(jrr[jss::status] == "success"); + + json::Value jvSubmit; + jvSubmit[jss::tx_json] = jrr[jss::tx_json]; + jrr = env.rpc("json", "submit_multisigned", to_string(jvSubmit))[jss::result]; + BEAST_EXPECT(jrr[jss::status] == "success"); + env.close(); + BEAST_EXPECT(env.balance(alice) == aliceBalance - XRPAmount(sendAmt)); + BEAST_EXPECT(env.balance(bob) == bobBalance - (10 * baseFee)); + BEAST_EXPECT(env.balance(daria) == dariaBalance); + BEAST_EXPECT(env.balance(carol) == carolBalance + XRPAmount(sendAmt)); + } + + // The delegated account bob attempts sign_for, will be rejected. + { + json::Value jv = makeDelegateTx(); + jv[jss::account] = bob.human(); + jv[jss::secret] = bob.name(); + auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result]; + BEAST_EXPECT(jrr[jss::status] == "error"); + BEAST_EXPECT( + jrr[jss::error_message].asString().find( + "A Signer may not be the transaction's Account") != std::string::npos); + } + } + void testPermissionValue(FeatureBitset features) { @@ -2086,6 +2230,8 @@ class Delegate_test : public beast::unit_test::Suite testSingleSignBadSecret(); testMultiSign(); testMultiSignQuorumNotMet(); + testMultiSignDelegatorAsSigner(); + testSignForDelegated(); testPermissionValue(all); testTxRequireFeatures(all); testTxDelegableCount(); diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 4c47914d66..9faa82dfe8 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -1255,7 +1255,9 @@ transactionSignFor( signers.emplaceBack(std::move(signer)); // The array must be sorted and validated. - auto err = sortAndValidateSigners(signers, (*sttx)[sfAccount]); + // For delegated transactions, the delegate account is + // the one forbidden from appearing in its own Signers array. + auto err = sortAndValidateSigners(signers, sttx->getFeePayer()); if (RPC::containsError(err)) return err; } @@ -1422,7 +1424,9 @@ transactionSubmitMultiSigned( } // The array must be sorted and validated. - auto err = sortAndValidateSigners(signers, srcAddressID); + // For delegated transactions, getFeePayer() returns sfDelegate, + // that account is the one forbidden from appearing in its own Signers array. + auto err = sortAndValidateSigners(signers, stTx->getFeePayer()); if (RPC::containsError(err)) return err; From 8c0080020f2a9c2d4b4dc7f6e059bf49b82ee867 Mon Sep 17 00:00:00 2001 From: Shawn Xie <35279399+shawnxie999@users.noreply.github.com> Date: Wed, 20 May 2026 17:10:04 -0400 Subject: [PATCH 05/87] fix: Update pDEX invariant firing under a valid offer deletion (#7118) Co-authored-by: Peter Chen --- .../tx/invariants/PermissionedDEXInvariant.h | 7 ++- .../invariants/PermissionedDEXInvariant.cpp | 10 +++- src/test/app/PermissionedDEX_test.cpp | 55 +++++++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/include/xrpl/tx/invariants/PermissionedDEXInvariant.h b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h index 3784a32840..654ed3e009 100644 --- a/include/xrpl/tx/invariants/PermissionedDEXInvariant.h +++ b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h @@ -10,9 +10,10 @@ namespace xrpl { class ValidPermissionedDEX { - bool regularOffers_ = false; - bool badHybridsOld_ = false; // pre-fixCleanup3_1_3: missing field/domain or size > 1 - bool badHybrids_ = false; // post-fixCleanup3_1_3: also catches size == 0 (size != 1) + bool regularOffersOld_ = false; // pre-fixCleanup3_2_0: also flags deleted offers + bool regularOffers_ = false; // post-fixCleanup3_2_0: excludes deleted offers + bool badHybridsOld_ = false; // pre-fixCleanup3_1_3: missing field/domain or size > 1 + bool badHybrids_ = false; // post-fixCleanup3_1_3: also catches size == 0 (size != 1) hash_set domains_; public: diff --git a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp index b480b90a31..282df85302 100644 --- a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp @@ -20,7 +20,7 @@ namespace xrpl { void ValidPermissionedDEX::visitEntry( - bool, + bool isDelete, std::shared_ptr const& before, std::shared_ptr const& after) { @@ -38,7 +38,9 @@ ValidPermissionedDEX::visitEntry( } else { - regularOffers_ = true; + regularOffersOld_ = true; + if (!isDelete) + regularOffers_ = true; } // pre-fixCleanup3_1_3: hybrid offer missing domain, missing @@ -100,7 +102,9 @@ ValidPermissionedDEX::finalize( } } - if (regularOffers_) + bool const hasRegularOffers = + view.rules().enabled(fixCleanup3_2_0) ? regularOffers_ : regularOffersOld_; + if (hasRegularOffers) { JLOG(j.fatal()) << "Invariant failed: domain transaction" " affected regular offers"; diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index be377c0c1d..3a95fb2f92 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include // IWYU pragma: keep #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include @@ -1455,6 +1457,54 @@ class PermissionedDEX_test : public beast::unit_test::Suite } } + void + testCancelRegularOfferWithDomainCreate(FeatureBitset features) + { + bool const fixEnabled = features[fixCleanup3_2_0]; + + testcase << "Cancel regular offer via domain OfferCreate" + << (fixEnabled ? " (fixCleanup3_2_0 enabled)" : " (fixCleanup3_2_0 disabled)"); + + // An OfferCreate with sfDomainID and sfOfferSequence pointing to + // the user's own non-domain offer should atomically cancel the + // regular offer and place the new domain offer. + // + // Pre-fixCleanup3_2_0: ValidPermissionedDEX flagged the deleted + // regular offer, so the transaction failed with tecINVARIANT_FAILED. + // Post-fixCleanup3_2_0: the invariant ignores deletions and the + // transaction succeeds. + + Env env(*this, features); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); + + auto const regularSeq = env.seq(bob); + env(offer(bob, XRP(10), USD(10))); + env.close(); + BEAST_EXPECT(checkOffer(env, bob, regularSeq, XRP(10), USD(10), 0, false)); + + auto const domainSeq = env.seq(bob); + if (fixEnabled) + { + env(offer(bob, XRP(20), USD(20)), + Domain(domainID), + Json(jss::OfferSequence, regularSeq)); + env.close(); + BEAST_EXPECT(!offerExists(env, bob, regularSeq)); + BEAST_EXPECT(checkOffer(env, bob, domainSeq, XRP(20), USD(20), 0, true)); + } + else + { + env(offer(bob, XRP(20), USD(20)), + Domain(domainID), + Json(jss::OfferSequence, regularSeq), + Ter(tecINVARIANT_FAILED)); + env.close(); + BEAST_EXPECT(offerExists(env, bob, regularSeq)); + BEAST_EXPECT(!offerExists(env, bob, domainSeq)); + } + } + public: void run() override @@ -1478,6 +1528,11 @@ public: testHybridOfferDirectories(all); testHybridMalformedOffer(all); testHybridMalformedOffer(all - fixCleanup3_1_3); + + // Cancelling a regular offer in a domain OfferCreate is allowed + // only after fixCleanup3_2_0. + testCancelRegularOfferWithDomainCreate(all); + testCancelRegularOfferWithDomainCreate(all - fixCleanup3_2_0); } }; From a830ab10efed8d3e59ef2fc15d66efdf9c6bb0d8 Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Wed, 20 May 2026 22:31:15 +0100 Subject: [PATCH 06/87] style: More clang-tidy identifier renaming (#7290) --- .clang-tidy | 3 + include/xrpl/basics/TaggedCache.h | 22 +- include/xrpl/basics/TaggedCache.ipp | 44 +-- include/xrpl/basics/scope.h | 58 ++-- include/xrpl/beast/unit_test/reporter.h | 22 +- include/xrpl/core/Coro.ipp | 10 +- include/xrpl/core/Job.h | 2 +- include/xrpl/core/JobQueue.h | 4 +- include/xrpl/ledger/BookDirs.h | 8 +- include/xrpl/ledger/OpenView.h | 2 +- include/xrpl/ledger/detail/RawStateTable.h | 10 +- include/xrpl/net/HTTPClientSSLContext.h | 12 +- include/xrpl/protocol/Book.h | 18 +- include/xrpl/protocol/ErrorCodes.h | 8 +- include/xrpl/protocol/STPathSet.h | 28 +- include/xrpl/protocol/STTx.h | 4 +- include/xrpl/resource/detail/Entry.h | 10 +- include/xrpl/resource/detail/Key.h | 4 +- include/xrpl/resource/detail/Logic.h | 32 +- include/xrpl/server/Handoff.h | 2 +- include/xrpl/server/Port.h | 48 +-- include/xrpl/server/detail/BaseHTTPPeer.h | 30 +- include/xrpl/server/detail/BasePeer.h | 4 +- include/xrpl/server/detail/BaseWSPeer.h | 40 +-- include/xrpl/server/detail/Door.h | 36 +-- include/xrpl/server/detail/PlainHTTPPeer.h | 12 +- include/xrpl/server/detail/SSLHTTPPeer.h | 22 +- include/xrpl/server/detail/SSLWSPeer.h | 6 +- include/xrpl/server/detail/ServerImpl.h | 12 +- src/libxrpl/basics/ResolverAsio.cpp | 30 +- src/libxrpl/beast/insight/StatsDCollector.cpp | 24 +- src/libxrpl/core/detail/Job.cpp | 4 +- src/libxrpl/core/detail/JobQueue.cpp | 4 +- src/libxrpl/ledger/BookDirs.cpp | 18 +- src/libxrpl/ledger/OpenView.cpp | 12 +- src/libxrpl/protocol/ErrorCodes.cpp | 2 +- src/libxrpl/protocol/STTx.cpp | 14 +- src/libxrpl/server/Port.cpp | 49 ++-- src/test/app/AMMMPT_test.cpp | 50 ++-- src/test/app/AMM_test.cpp | 6 +- src/test/app/FeeVote_test.cpp | 82 +++--- src/test/app/LedgerHistory_test.cpp | 2 +- src/test/app/LedgerLoad_test.cpp | 6 +- src/test/app/LedgerMaster_test.cpp | 4 +- src/test/app/LedgerReplay_test.cpp | 8 +- src/test/app/LoadFeeTrack_test.cpp | 6 +- src/test/app/MPToken_test.cpp | 2 +- src/test/app/NetworkID_test.cpp | 2 +- src/test/app/PathMPT_test.cpp | 6 +- src/test/app/Path_test.cpp | 6 +- src/test/app/RCLValidations_test.cpp | 4 +- src/test/app/Regression_test.cpp | 4 +- src/test/app/SHAMapStore_test.cpp | 2 +- src/test/app/Transaction_ordering_test.cpp | 4 +- src/test/app/TxQ_test.cpp | 8 +- src/test/app/ValidatorList_test.cpp | 4 +- src/test/app/XChain_test.cpp | 230 ++++++++------- src/test/consensus/Consensus_test.cpp | 4 +- src/test/consensus/NegativeUNL_test.cpp | 152 +++++----- src/test/consensus/Validations_test.cpp | 24 +- src/test/core/Config_test.cpp | 114 ++++---- src/test/core/Coroutine_test.cpp | 4 +- src/test/csf/Scheduler.h | 14 +- src/test/jtx/impl/AMMTest.cpp | 6 +- src/test/jtx/impl/Env.cpp | 2 +- src/test/jtx/impl/JSONRPCClient.cpp | 8 +- src/test/jtx/impl/TestHelpers.cpp | 6 +- src/test/jtx/impl/WSClient.cpp | 8 +- src/test/jtx/impl/envconfig.cpp | 12 +- src/test/jtx/impl/permissioned_dex.cpp | 6 +- src/test/jtx/impl/xchain_bridge.cpp | 23 +- src/test/jtx/permissioned_dex.h | 2 +- src/test/jtx/xchain_bridge.h | 18 +- src/test/ledger/SkipList_test.cpp | 2 +- src/test/ledger/View_test.cpp | 8 +- src/test/nodestore/Database_test.cpp | 6 +- src/test/nodestore/Timing_test.cpp | 24 +- src/test/overlay/compression_test.cpp | 21 +- src/test/overlay/reduce_relay_test.cpp | 44 ++- src/test/overlay/short_read_test.cpp | 22 +- src/test/overlay/tx_reduce_relay_test.cpp | 16 +- src/test/peerfinder/PeerFinder_test.cpp | 4 +- src/test/rpc/AccountTx_test.cpp | 4 +- src/test/rpc/Book_test.cpp | 4 +- src/test/rpc/DeliveredAmount_test.cpp | 4 +- src/test/rpc/JSONRPC_test.cpp | 4 +- src/test/rpc/KeyGeneration_test.cpp | 104 +++---- src/test/rpc/LedgerClosed_test.cpp | 2 +- src/test/rpc/LedgerEntry_test.cpp | 6 +- src/test/rpc/LedgerRPC_test.cpp | 4 +- src/test/rpc/LedgerRequest_test.cpp | 6 +- src/test/rpc/NoRippleCheck_test.cpp | 2 +- src/test/rpc/RPCCall_test.cpp | 2 +- src/test/rpc/Simulate_test.cpp | 6 +- src/test/rpc/Subscribe_test.cpp | 4 +- src/test/rpc/TransactionEntry_test.cpp | 4 +- src/test/rpc/Transaction_test.cpp | 6 +- src/test/rpc/Version_test.cpp | 12 +- src/test/server/ServerStatus_test.cpp | 6 +- src/test/server/Server_test.cpp | 8 +- src/test/unit_test/multi_runner.cpp | 114 ++++---- src/test/unit_test/multi_runner.h | 40 +-- src/xrpld/app/ledger/LedgerHistory.cpp | 43 ++- src/xrpld/app/ledger/LedgerHistory.h | 12 +- src/xrpld/app/ledger/LedgerMaster.h | 12 +- src/xrpld/app/ledger/OpenLedger.h | 4 +- src/xrpld/app/ledger/detail/LedgerCleaner.cpp | 2 +- src/xrpld/app/ledger/detail/LedgerMaster.cpp | 41 ++- src/xrpld/app/ledger/detail/OpenLedger.cpp | 12 +- src/xrpld/app/main/Application.cpp | 64 ++-- src/xrpld/app/main/BasicApp.cpp | 4 +- src/xrpld/app/main/BasicApp.h | 4 +- src/xrpld/app/main/Main.cpp | 34 +-- src/xrpld/app/misc/FeeVoteImpl.cpp | 21 +- src/xrpld/app/misc/NetworkOPs.cpp | 99 ++++--- src/xrpld/app/misc/SHAMapStoreImp.cpp | 24 +- src/xrpld/app/misc/SHAMapStoreImp.h | 6 +- src/xrpld/app/misc/ValidatorSite.h | 8 +- src/xrpld/app/misc/detail/ValidatorSite.cpp | 32 +- src/xrpld/app/misc/detail/WorkSSL.cpp | 6 +- src/xrpld/consensus/Consensus.cpp | 26 +- src/xrpld/consensus/Consensus.h | 22 +- src/xrpld/consensus/ConsensusParms.h | 28 +- src/xrpld/consensus/DisputedTx.h | 12 +- src/xrpld/consensus/Validations.h | 24 +- src/xrpld/core/Config.h | 130 ++++----- src/xrpld/core/detail/Config.cpp | 276 +++++++++--------- src/xrpld/overlay/Message.h | 2 +- src/xrpld/overlay/Slot.h | 4 +- src/xrpld/overlay/detail/ConnectAttempt.cpp | 8 +- src/xrpld/overlay/detail/Handshake.cpp | 12 +- src/xrpld/overlay/detail/Message.cpp | 2 +- src/xrpld/overlay/detail/OverlayImpl.cpp | 48 +-- src/xrpld/overlay/detail/OverlayImpl.h | 6 +- src/xrpld/overlay/detail/PeerImp.cpp | 32 +- src/xrpld/overlay/detail/PeerImp.h | 20 +- src/xrpld/overlay/detail/ProtocolMessage.h | 66 ++--- src/xrpld/overlay/detail/TxMetrics.cpp | 6 +- src/xrpld/overlay/detail/TxMetrics.h | 2 +- src/xrpld/peerfinder/detail/Counts.h | 54 ++-- .../peerfinder/detail/PeerfinderConfig.cpp | 14 +- src/xrpld/peerfinder/detail/SlotImp.cpp | 10 +- src/xrpld/peerfinder/detail/SlotImp.h | 24 +- src/xrpld/rpc/ServerHandler.h | 10 +- src/xrpld/rpc/detail/AssetCache.h | 8 +- src/xrpld/rpc/detail/PathRequest.cpp | 56 ++-- src/xrpld/rpc/detail/PathRequest.h | 6 +- src/xrpld/rpc/detail/Pathfinder.cpp | 12 +- src/xrpld/rpc/detail/Pathfinder.h | 2 +- src/xrpld/rpc/detail/RPCCall.cpp | 14 +- src/xrpld/rpc/detail/RPCHandler.cpp | 2 +- src/xrpld/rpc/detail/RPCSub.cpp | 6 +- src/xrpld/rpc/detail/Role.cpp | 12 +- src/xrpld/rpc/detail/ServerHandler.cpp | 56 ++-- src/xrpld/rpc/detail/TransactionSign.cpp | 18 +- src/xrpld/rpc/detail/WSInfoSub.h | 4 +- src/xrpld/rpc/handlers/orderbook/PathFind.cpp | 2 +- .../rpc/handlers/orderbook/RipplePathFind.cpp | 2 +- src/xrpld/rpc/handlers/server_info/Version.h | 2 +- src/xrpld/rpc/json_body.h | 6 +- 160 files changed, 1726 insertions(+), 1746 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index a444957a0e..ee6bde6eba 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -191,8 +191,11 @@ CheckOptions: readability-identifier-naming.ParameterCase: camelBack readability-identifier-naming.FunctionCase: camelBack readability-identifier-naming.MemberCase: camelBack + readability-identifier-naming.PrivateMemberCase: camelBack readability-identifier-naming.PrivateMemberSuffix: _ + readability-identifier-naming.ProtectedMemberCase: camelBack readability-identifier-naming.ProtectedMemberSuffix: _ + readability-identifier-naming.PublicMemberCase: camelBack readability-identifier-naming.PublicMemberSuffix: "" readability-identifier-naming.GlobalFunctionIgnoredRegexp: "^(to_string|hash_append|tuple_hash)$" diff --git a/include/xrpl/basics/TaggedCache.h b/include/xrpl/basics/TaggedCache.h index 52b866cdad..fca9eabde2 100644 --- a/include/xrpl/basics/TaggedCache.h +++ b/include/xrpl/basics/TaggedCache.h @@ -181,14 +181,14 @@ private: beast::insight::Collector::ptr const& collector) : hook(collector->makeHook(handler)) , size(collector->makeGauge(prefix, "size")) - , hit_rate(collector->makeGauge(prefix, "hit_rate")) + , hitRate(collector->makeGauge(prefix, "hit_rate")) { } beast::insight::Hook hook; beast::insight::Gauge size; - beast::insight::Gauge hit_rate; + beast::insight::Gauge hitRate; std::size_t hits{0}; std::size_t misses{0}; @@ -197,16 +197,16 @@ private: class KeyOnlyEntry { public: - clock_type::time_point last_access; + clock_type::time_point lastAccess; - explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : last_access(lastAccess) + explicit KeyOnlyEntry(clock_type::time_point const& lastAccess) : lastAccess(lastAccess) { } void touch(clock_type::time_point const& now) { - last_access = now; + lastAccess = now; } }; @@ -214,10 +214,10 @@ private: { public: shared_weak_combo_pointer_type ptr; - clock_type::time_point last_access; + clock_type::time_point lastAccess; ValueEntry(clock_type::time_point const& lastAccess, shared_pointer_type const& ptr) - : ptr(ptr), last_access(lastAccess) + : ptr(ptr), lastAccess(lastAccess) { } @@ -246,7 +246,7 @@ private: void touch(clock_type::time_point const& now) { - last_access = now; + lastAccess = now; } }; @@ -286,13 +286,13 @@ private: std::string name_; // Desired number of cache entries (0 = ignore) - int const target_size_; + int const targetSize_; // Desired maximum cache age - clock_type::duration const target_age_; + clock_type::duration const targetAge_; // Number of items cached - int cache_count_{0}; + int cacheCount_{0}; cache_type cache_; // Hold strong reference to recent objects std::uint64_t hits_{0}; std::uint64_t misses_{0}; diff --git a/include/xrpl/basics/TaggedCache.ipp b/include/xrpl/basics/TaggedCache.ipp index 8ef9e16570..cee02749c6 100644 --- a/include/xrpl/basics/TaggedCache.ipp +++ b/include/xrpl/basics/TaggedCache.ipp @@ -34,8 +34,8 @@ inline TaggedCache< , clock_(clock) , stats_(name, std::bind(&TaggedCache::collectMetrics, this), collector) , name_(name) - , target_size_(size) - , target_age_(expiration) + , targetSize_(size) + , targetAge_(expiration) { } @@ -86,7 +86,7 @@ TaggedCache(cache_.size()) <= target_size_)) + if (targetSize_ == 0 || (static_cast(cache_.size()) <= targetSize_)) { - whenExpire = now - target_age_; + whenExpire = now - targetAge_; } else { - whenExpire = now - (target_age_ * target_size_ / cache_.size()); + whenExpire = now - (targetAge_ * targetSize_ / cache_.size()); clock_type::duration const minimumAge(std::chrono::seconds(1)); if (whenExpire > (now - minimumAge)) whenExpire = now - minimumAge; JLOG(journal_.trace()) - << name_ << " is growing fast " << cache_.size() << " of " << target_size_ - << " aging at " << (now - whenExpire).count() << " of " << target_age_.count(); + << name_ << " is growing fast " << cache_.size() << " of " << targetSize_ + << " aging at " << (now - whenExpire).count() << " of " << targetAge_.count(); } std::vector workers; @@ -242,7 +242,7 @@ TaggedCachesecond.last_access = now; + it->second.lastAccess = now; return inserted; } @@ -626,7 +626,7 @@ TaggedCachesecond.last_access <= whenExpire) + else if (cit->second.lastAccess <= whenExpire) { // strong, expired ++cacheRemovals; @@ -773,12 +773,12 @@ TaggedCachesecond.last_access > now) + if (cit->second.lastAccess > now) { - cit->second.last_access = now; + cit->second.lastAccess = now; ++cit; } - else if (cit->second.last_access <= whenExpire) + else if (cit->second.lastAccess <= whenExpire) { cit = partition.erase(cit); } diff --git a/include/xrpl/basics/scope.h b/include/xrpl/basics/scope.h index cd2a5299b2..cfd21e6e30 100644 --- a/include/xrpl/basics/scope.h +++ b/include/xrpl/basics/scope.h @@ -24,20 +24,20 @@ namespace xrpl { template class ScopeExit { - EF exit_function_; - bool execute_on_destruction_{true}; + EF exitFunction_; + bool executeOnDestruction_{true}; public: ~ScopeExit() { - if (execute_on_destruction_) - exit_function_(); + if (executeOnDestruction_) + exitFunction_(); } ScopeExit(ScopeExit&& rhs) noexcept( std::is_nothrow_move_constructible_v || std::is_nothrow_copy_constructible_v) - : exit_function_{std::forward(rhs.exit_function_)} - , execute_on_destruction_{rhs.execute_on_destruction_} + : exitFunction_{std::forward(rhs.exitFunction_)} + , executeOnDestruction_{rhs.executeOnDestruction_} { rhs.release(); } @@ -51,7 +51,7 @@ public: std::enable_if_t< !std::is_same_v, ScopeExit> && std::is_constructible_v>* = 0) noexcept - : exit_function_{std::forward(f)} + : exitFunction_{std::forward(f)} { static_assert(std::is_nothrow_constructible_v(f))>); } @@ -59,7 +59,7 @@ public: void release() noexcept { - execute_on_destruction_ = false; + executeOnDestruction_ = false; } }; @@ -69,22 +69,22 @@ ScopeExit(EF) -> ScopeExit; template class ScopeFail { - EF exit_function_; - bool execute_on_destruction_{true}; - int uncaught_on_creation_{std::uncaught_exceptions()}; + EF exitFunction_; + bool executeOnDestruction_{true}; + int uncaughtOnCreation_{std::uncaught_exceptions()}; public: ~ScopeFail() { - if (execute_on_destruction_ && std::uncaught_exceptions() > uncaught_on_creation_) - exit_function_(); + if (executeOnDestruction_ && std::uncaught_exceptions() > uncaughtOnCreation_) + exitFunction_(); } ScopeFail(ScopeFail&& rhs) noexcept( std::is_nothrow_move_constructible_v || std::is_nothrow_copy_constructible_v) - : exit_function_{std::forward(rhs.exit_function_)} - , execute_on_destruction_{rhs.execute_on_destruction_} - , uncaught_on_creation_{rhs.uncaught_on_creation_} + : exitFunction_{std::forward(rhs.exitFunction_)} + , executeOnDestruction_{rhs.executeOnDestruction_} + , uncaughtOnCreation_{rhs.uncaughtOnCreation_} { rhs.release(); } @@ -98,7 +98,7 @@ public: std::enable_if_t< !std::is_same_v, ScopeFail> && std::is_constructible_v>* = 0) noexcept - : exit_function_{std::forward(f)} + : exitFunction_{std::forward(f)} { static_assert(std::is_nothrow_constructible_v(f))>); } @@ -106,7 +106,7 @@ public: void release() noexcept { - execute_on_destruction_ = false; + executeOnDestruction_ = false; } }; @@ -116,22 +116,22 @@ ScopeFail(EF) -> ScopeFail; template class ScopeSuccess { - EF exit_function_; - bool execute_on_destruction_{true}; - int uncaught_on_creation_{std::uncaught_exceptions()}; + EF exitFunction_; + bool executeOnDestruction_{true}; + int uncaughtOnCreation_{std::uncaught_exceptions()}; public: - ~ScopeSuccess() noexcept(noexcept(exit_function_())) + ~ScopeSuccess() noexcept(noexcept(exitFunction_())) { - if (execute_on_destruction_ && std::uncaught_exceptions() <= uncaught_on_creation_) - exit_function_(); + if (executeOnDestruction_ && std::uncaught_exceptions() <= uncaughtOnCreation_) + exitFunction_(); } ScopeSuccess(ScopeSuccess&& rhs) noexcept( std::is_nothrow_move_constructible_v || std::is_nothrow_copy_constructible_v) - : exit_function_{std::forward(rhs.exit_function_)} - , execute_on_destruction_{rhs.execute_on_destruction_} - , uncaught_on_creation_{rhs.uncaught_on_creation_} + : exitFunction_{std::forward(rhs.exitFunction_)} + , executeOnDestruction_{rhs.executeOnDestruction_} + , uncaughtOnCreation_{rhs.uncaughtOnCreation_} { rhs.release(); } @@ -146,14 +146,14 @@ public: !std::is_same_v, ScopeSuccess> && std::is_constructible_v>* = 0) noexcept(std::is_nothrow_constructible_v || std::is_nothrow_constructible_v) - : exit_function_{std::forward(f)} + : exitFunction_{std::forward(f)} { } void release() noexcept { - execute_on_destruction_ = false; + executeOnDestruction_ = false; } }; diff --git a/include/xrpl/beast/unit_test/reporter.h b/include/xrpl/beast/unit_test/reporter.h index b4b6c69fa4..1fdbb451a6 100644 --- a/include/xrpl/beast/unit_test/reporter.h +++ b/include/xrpl/beast/unit_test/reporter.h @@ -77,8 +77,8 @@ private: std::ostream& os_; Results results_; - SuiteResults suite_results_; - CaseResults case_results_; + SuiteResults suiteResults_; + CaseResults caseResults_; public: Reporter(Reporter const&) = delete; @@ -196,22 +196,22 @@ template void Reporter::onSuiteBegin(SuiteInfo const& info) { - suite_results_ = SuiteResults{info.fullName()}; + suiteResults_ = SuiteResults{info.fullName()}; } template void Reporter::onSuiteEnd() { - results_.add(suite_results_); + results_.add(suiteResults_); } template void Reporter::onCaseBegin(std::string const& name) { - case_results_ = CaseResults(name); - os_ << suite_results_.name << (case_results_.name.empty() ? "" : (" " + case_results_.name)) + caseResults_ = CaseResults(name); + os_ << suiteResults_.name << (caseResults_.name.empty() ? "" : (" " + caseResults_.name)) << std::endl; } @@ -219,23 +219,23 @@ template void Reporter::onCaseEnd() { - suite_results_.add(case_results_); + suiteResults_.add(caseResults_); } template void Reporter::onPass() { - ++case_results_.total; + ++caseResults_.total; } template void Reporter::onFail(std::string const& reason) { - ++case_results_.failed; - ++case_results_.total; - os_ << "#" << case_results_.total << " failed" << (reason.empty() ? "" : ": ") << reason + ++caseResults_.failed; + ++caseResults_.total; + os_ << "#" << caseResults_.total << " failed" << (reason.empty() ? "" : ": ") << reason << std::endl; } diff --git a/include/xrpl/core/Coro.ipp b/include/xrpl/core/Coro.ipp index 90fa965038..133caf37a9 100644 --- a/include/xrpl/core/Coro.ipp +++ b/include/xrpl/core/Coro.ipp @@ -47,7 +47,7 @@ inline bool JobQueue::Coro::post() { { - std::scoped_lock const lk(mutex_run_); + std::scoped_lock const lk(mutexRun_); running_ = true; } @@ -58,7 +58,7 @@ JobQueue::Coro::post() } // The coroutine will not run. Clean up running_. - std::scoped_lock const lk(mutex_run_); + std::scoped_lock const lk(mutexRun_); running_ = false; cv_.notify_all(); return false; @@ -68,7 +68,7 @@ inline void JobQueue::Coro::resume() { { - std::scoped_lock const lk(mutex_run_); + std::scoped_lock const lk(mutexRun_); running_ = true; } { @@ -92,7 +92,7 @@ JobQueue::Coro::resume() } detail::getLocalValues().release(); detail::getLocalValues().reset(saved); - std::scoped_lock const lk(mutex_run_); + std::scoped_lock const lk(mutexRun_); running_ = false; cv_.notify_all(); } @@ -127,7 +127,7 @@ JobQueue::Coro::expectEarlyExit() inline void JobQueue::Coro::join() { - std::unique_lock lk(mutex_run_); + std::unique_lock lk(mutexRun_); cv_.wait(lk, [this]() { return !running_; }); } diff --git a/include/xrpl/core/Job.h b/include/xrpl/core/Job.h index b62488dec6..6af32eb2d8 100644 --- a/include/xrpl/core/Job.h +++ b/include/xrpl/core/Job.h @@ -127,7 +127,7 @@ private: std::function job_; std::shared_ptr loadEvent_; std::string name_; - clock_type::time_point queue_time_; + clock_type::time_point queueTime_; }; using JobCounter = ClosureCounter; diff --git a/include/xrpl/core/JobQueue.h b/include/xrpl/core/JobQueue.h index 2a96ea45fa..fc15e9a064 100644 --- a/include/xrpl/core/JobQueue.h +++ b/include/xrpl/core/JobQueue.h @@ -52,7 +52,7 @@ public: std::string name_; bool running_{false}; std::mutex mutex_; - std::mutex mutex_run_; + std::mutex mutexRun_; std::condition_variable cv_; boost::coroutines2::coroutine::push_type* yield_{}; boost::coroutines2::coroutine::pull_type coro_; @@ -246,7 +246,7 @@ private: // Statistics tracking perf::PerfLog& perfLog_; beast::insight::Collector::ptr collector_; - beast::insight::Gauge job_count_; + beast::insight::Gauge jobCount_; beast::insight::Hook hook_; std::condition_variable cv_; diff --git a/include/xrpl/ledger/BookDirs.h b/include/xrpl/ledger/BookDirs.h index afde6ee7b3..7eaede14ec 100644 --- a/include/xrpl/ledger/BookDirs.h +++ b/include/xrpl/ledger/BookDirs.h @@ -9,7 +9,7 @@ class BookDirs private: ReadView const* view_ = nullptr; uint256 const root_; - uint256 const next_quality_; + uint256 const nextQuality_; uint256 const key_; std::shared_ptr sle_ = nullptr; unsigned int entry_ = 0; @@ -67,15 +67,15 @@ private: friend class BookDirs; const_iterator(ReadView const& view, uint256 const& root, uint256 const& dirKey) - : view_(&view), root_(root), key_(dirKey), cur_key_(dirKey) + : view_(&view), root_(root), key_(dirKey), curKey_(dirKey) { } ReadView const* view_ = nullptr; uint256 root_; - uint256 next_quality_; + uint256 nextQuality_; uint256 key_; - uint256 cur_key_; + uint256 curKey_; std::shared_ptr sle_; unsigned int entry_ = 0; uint256 index_; diff --git a/include/xrpl/ledger/OpenView.h b/include/xrpl/ledger/OpenView.h index c7f80ee321..18d1a9399c 100644 --- a/include/xrpl/ledger/OpenView.h +++ b/include/xrpl/ledger/OpenView.h @@ -76,7 +76,7 @@ private: // monotonic_resource_ must outlive `items_`. Make a pointer so it may be // easily moved. - std::unique_ptr monotonic_resource_; + std::unique_ptr monotonicResource_; txs_map txs_; Rules rules_; LedgerHeader header_; diff --git a/include/xrpl/ledger/detail/RawStateTable.h b/include/xrpl/ledger/detail/RawStateTable.h index 53532ee5ec..e4329bf6fc 100644 --- a/include/xrpl/ledger/detail/RawStateTable.h +++ b/include/xrpl/ledger/detail/RawStateTable.h @@ -22,14 +22,14 @@ public: static constexpr size_t kInitialBufferSize = kilobytes(256); RawStateTable() - : monotonic_resource_{std::make_unique( + : monotonicResource_{std::make_unique( kInitialBufferSize)} - , items_{monotonic_resource_.get()} {}; + , items_{monotonicResource_.get()} {}; RawStateTable(RawStateTable const& rhs) - : monotonic_resource_{std::make_unique( + : monotonicResource_{std::make_unique( kInitialBufferSize)} - , items_{rhs.items_, monotonic_resource_.get()} + , items_{rhs.items_, monotonicResource_.get()} , dropsDestroyed_{rhs.dropsDestroyed_} {}; RawStateTable(RawStateTable&&) = default; @@ -101,7 +101,7 @@ private: boost::container::pmr::polymorphic_allocator>>; // monotonic_resource_ must outlive `items_`. Make a pointer so it may be // easily moved. - std::unique_ptr monotonic_resource_; + std::unique_ptr monotonicResource_; items_t items_; XRPAmount dropsDestroyed_{0}; diff --git a/include/xrpl/net/HTTPClientSSLContext.h b/include/xrpl/net/HTTPClientSSLContext.h index d1744bbce4..ca1983f141 100644 --- a/include/xrpl/net/HTTPClientSSLContext.h +++ b/include/xrpl/net/HTTPClientSSLContext.h @@ -21,13 +21,13 @@ public: bool sslVerify, beast::Journal j, boost::asio::ssl::context_base::method method = boost::asio::ssl::context::sslv23) - : ssl_context_{method}, j_(j), verify_{sslVerify} + : sslContext_{method}, j_(j), verify_{sslVerify} { boost::system::error_code ec; if (sslVerifyFile.empty()) { - registerSSLCerts(ssl_context_, ec, j_); + registerSSLCerts(sslContext_, ec, j_); if (ec && sslVerifyDir.empty()) { @@ -37,12 +37,12 @@ public: } else { - ssl_context_.load_verify_file(sslVerifyFile); + sslContext_.load_verify_file(sslVerifyFile); } if (!sslVerifyDir.empty()) { - ssl_context_.add_verify_path(sslVerifyDir, ec); + sslContext_.add_verify_path(sslVerifyDir, ec); if (ec) { @@ -55,7 +55,7 @@ public: boost::asio::ssl::context& context() { - return ssl_context_; + return sslContext_; } [[nodiscard]] bool @@ -153,7 +153,7 @@ public: } private: - boost::asio::ssl::context ssl_context_; + boost::asio::ssl::context sslContext_; beast::Journal const j_; bool const verify_; }; diff --git a/include/xrpl/protocol/Book.h b/include/xrpl/protocol/Book.h index fc36abddc4..01dc40075b 100644 --- a/include/xrpl/protocol/Book.h +++ b/include/xrpl/protocol/Book.h @@ -140,8 +140,8 @@ private: using issue_hasher = std::hash; using mptissue_hasher = std::hash; - issue_hasher m_issue_hasher_; - mptissue_hasher m_mptissue_hasher_; + issue_hasher mIssueHasher_; + mptissue_hasher mMptissueHasher_; public: explicit hash() = default; @@ -151,11 +151,11 @@ public: { return asset.visit( [&](xrpl::Issue const& issue) { - value_type const result(m_issue_hasher_(issue)); + value_type const result(mIssueHasher_(issue)); return result; }, [&](xrpl::MPTIssue const& issue) { - value_type const result(m_mptissue_hasher_(issue)); + value_type const result(mMptissueHasher_(issue)); return result; }); } @@ -170,8 +170,8 @@ private: using asset_hasher = std::hash; using uint256_hasher = xrpl::uint256::hasher; - asset_hasher issue_hasher_; - uint256_hasher uint256_hasher_; + asset_hasher issueHasher_; + uint256_hasher uint256Hasher_; public: hash() = default; @@ -182,11 +182,11 @@ public: value_type operator()(argument_type const& value) const { - value_type result(issue_hasher_(value.in)); - boost::hash_combine(result, issue_hasher_(value.out)); + value_type result(issueHasher_(value.in)); + boost::hash_combine(result, issueHasher_(value.out)); if (value.domain) - boost::hash_combine(result, uint256_hasher_(*value.domain)); + boost::hash_combine(result, uint256Hasher_(*value.domain)); return result; } diff --git a/include/xrpl/protocol/ErrorCodes.h b/include/xrpl/protocol/ErrorCodes.h index a91adfc55a..f5e67fd572 100644 --- a/include/xrpl/protocol/ErrorCodes.h +++ b/include/xrpl/protocol/ErrorCodes.h @@ -172,24 +172,24 @@ struct ErrorInfo { // Default ctor needed to produce an empty std::array during constexpr eval. constexpr ErrorInfo() - : code(RpcUnknown), token("unknown"), message("An unknown error code."), http_status(200) + : code(RpcUnknown), token("unknown"), message("An unknown error code."), httpStatus(200) { } constexpr ErrorInfo(ErrorCodeI code, char const* token, char const* message) - : code(code), token(token), message(message), http_status(200) + : code(code), token(token), message(message), httpStatus(200) { } constexpr ErrorInfo(ErrorCodeI code, char const* token, char const* message, int httpStatus) - : code(code), token(token), message(message), http_status(httpStatus) + : code(code), token(token), message(message), httpStatus(httpStatus) { } ErrorCodeI code; json::StaticString token; json::StaticString message; - int http_status; + int httpStatus; }; /** Returns an ErrorInfo that reflects the error code. */ diff --git a/include/xrpl/protocol/STPathSet.h b/include/xrpl/protocol/STPathSet.h index a1891164f6..1508dcb727 100644 --- a/include/xrpl/protocol/STPathSet.h +++ b/include/xrpl/protocol/STPathSet.h @@ -21,8 +21,8 @@ class STPathElement final : public CountedObject PathAsset assetID_; AccountID issuerID_; - bool is_offer_; - std::size_t hash_value_; + bool isOffer_; + std::size_t hashValue_; public: // Bitwise values (typeCurrency | typeMPT) @@ -235,9 +235,9 @@ private: // ------------ STPathElement ------------ -inline STPathElement::STPathElement() : type_(TypeNone), is_offer_(true) +inline STPathElement::STPathElement() : type_(TypeNone), isOffer_(true) { - hash_value_ = getHash(*this); + hashValue_ = getHash(*this); } inline STPathElement::STPathElement( @@ -248,11 +248,11 @@ inline STPathElement::STPathElement( { if (!account) { - is_offer_ = true; + isOffer_ = true; } else { - is_offer_ = false; + isOffer_ = false; accountID_ = *account; type_ |= TypeAccount; XRPL_ASSERT( @@ -272,7 +272,7 @@ inline STPathElement::STPathElement( XRPL_ASSERT(issuerID_ != noAccount(), "xrpl::STPathElement::STPathElement : issuer is set"); } - hash_value_ = getHash(*this); + hashValue_ = getHash(*this); } inline STPathElement::STPathElement( @@ -284,9 +284,9 @@ inline STPathElement::STPathElement( , accountID_(account) , assetID_(asset) , issuerID_(issuer) - , is_offer_(isXRP(accountID_)) + , isOffer_(isXRP(accountID_)) { - if (!is_offer_) + if (!isOffer_) type_ |= TypeAccount; if (forceAsset || !isXRP(assetID_)) @@ -295,7 +295,7 @@ inline STPathElement::STPathElement( if (!isXRP(issuer)) type_ |= TypeIssuer; - hash_value_ = getHash(*this); + hashValue_ = getHash(*this); } inline STPathElement::STPathElement( @@ -307,12 +307,12 @@ inline STPathElement::STPathElement( , accountID_(account) , assetID_(asset) , issuerID_(issuer) - , is_offer_(isXRP(accountID_)) + , isOffer_(isXRP(accountID_)) { assetID_.visit( [&](Currency const&) { type_ = type_ & (~Type::TypeMpt); }, [&](MPTID const&) { type_ = type_ & (~Type::TypeCurrency); }); - hash_value_ = getHash(*this); + hashValue_ = getHash(*this); } inline auto @@ -324,7 +324,7 @@ STPathElement::getNodeType() const inline bool STPathElement::isOffer() const { - return is_offer_; + return isOffer_; } inline bool @@ -404,7 +404,7 @@ STPathElement::getIssuerID() const inline bool STPathElement::operator==(STPathElement const& t) const { - return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hash_value_ == t.hash_value_ && + return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hashValue_ == t.hashValue_ && accountID_ == t.accountID_ && assetID_ == t.assetID_ && issuerID_ == t.issuerID_; } diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index 5aa70d1299..4deedfafb7 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -27,7 +27,7 @@ enum class TxnSql : char { class STTx final : public STObject, public CountedObject { uint256 tid_; - TxType tx_type_; + TxType txType_; public: static constexpr std::size_t kMinMultiSigners = 1; @@ -187,7 +187,7 @@ inline STTx::STTx(SerialIter&& sit) // NOLINT(cppcoreguidelines-rvalue-referenc inline TxType STTx::getTxnType() const { - return tx_type_; + return txType_; } inline Blob diff --git a/include/xrpl/resource/detail/Entry.h b/include/xrpl/resource/detail/Entry.h index b12bbf36ae..361c8409c8 100644 --- a/include/xrpl/resource/detail/Entry.h +++ b/include/xrpl/resource/detail/Entry.h @@ -21,7 +21,7 @@ struct Entry : public beast::List::Node @param now Construction time of Entry. */ explicit Entry(clock_type::time_point const now) - : refcount(0), local_balance(now), remote_balance(0) + : refcount(0), localBalance(now), remoteBalance(0) { } @@ -46,7 +46,7 @@ struct Entry : public beast::List::Node int balance(clock_type::time_point const now) { - return local_balance.value(now) + remote_balance; + return localBalance.value(now) + remoteBalance; } // Add a charge and return normalized balance @@ -54,7 +54,7 @@ struct Entry : public beast::List::Node int add(int charge, clock_type::time_point const now) { - return local_balance.add(charge, now) + remote_balance; + return localBalance.add(charge, now) + remoteBalance; } // The public key of the peer @@ -67,10 +67,10 @@ struct Entry : public beast::List::Node int refcount; // Exponentially decaying balance of resource consumption - DecayingSample local_balance; + DecayingSample localBalance; // Normalized balance contribution from imports - int remote_balance; + int remoteBalance; // Time of the last warning clock_type::time_point lastWarningTime; diff --git a/include/xrpl/resource/detail/Key.h b/include/xrpl/resource/detail/Key.h index 935d44425e..7d24b33955 100644 --- a/include/xrpl/resource/detail/Key.h +++ b/include/xrpl/resource/detail/Key.h @@ -25,11 +25,11 @@ struct Key std::size_t operator()(Key const& v) const { - return addr_hash_(v.address); + return addrHash_(v.address); } private: - beast::Uhash<> addr_hash_; + beast::Uhash<> addrHash_; }; struct KeyEqual diff --git a/include/xrpl/resource/detail/Logic.h b/include/xrpl/resource/detail/Logic.h index 7e63baca6c..b11ac100f5 100644 --- a/include/xrpl/resource/detail/Logic.h +++ b/include/xrpl/resource/detail/Logic.h @@ -194,34 +194,34 @@ public: for (auto& inboundEntry : inbound_) { - int const localBalance = inboundEntry.local_balance.value(now); - if ((localBalance + inboundEntry.remote_balance) >= threshold) + int const localBalance = inboundEntry.localBalance.value(now); + if ((localBalance + inboundEntry.remoteBalance) >= threshold) { json::Value& entry = (ret[inboundEntry.toString()] = json::ValueType::Object); entry[jss::local] = localBalance; - entry[jss::remote] = inboundEntry.remote_balance; + entry[jss::remote] = inboundEntry.remoteBalance; entry[jss::type] = "inbound"; } } for (auto& outboundEntry : outbound_) { - int const localBalance = outboundEntry.local_balance.value(now); - if ((localBalance + outboundEntry.remote_balance) >= threshold) + int const localBalance = outboundEntry.localBalance.value(now); + if ((localBalance + outboundEntry.remoteBalance) >= threshold) { json::Value& entry = (ret[outboundEntry.toString()] = json::ValueType::Object); entry[jss::local] = localBalance; - entry[jss::remote] = outboundEntry.remote_balance; + entry[jss::remote] = outboundEntry.remoteBalance; entry[jss::type] = "outbound"; } } for (auto& adminEntry : admin_) { - int const localBalance = adminEntry.local_balance.value(now); - if ((localBalance + adminEntry.remote_balance) >= threshold) + int const localBalance = adminEntry.localBalance.value(now); + if ((localBalance + adminEntry.remoteBalance) >= threshold) { json::Value& entry = (ret[adminEntry.toString()] = json::ValueType::Object); entry[jss::local] = localBalance; - entry[jss::remote] = adminEntry.remote_balance; + entry[jss::remote] = adminEntry.remoteBalance; entry[jss::type] = "admin"; } } @@ -242,7 +242,7 @@ public: for (auto& inboundEntry : inbound_) { Gossip::Item item; - item.balance = inboundEntry.local_balance.value(now); + item.balance = inboundEntry.localBalance.value(now); if (item.balance >= kMinimumGossipBalance) { item.address = inboundEntry.key->address; @@ -278,7 +278,7 @@ public: Import::Item item; item.balance = gossipItem.balance; item.consumer = newInboundEndpoint(gossipItem.address); - item.consumer.entry().remote_balance += item.balance; + item.consumer.entry().remoteBalance += item.balance; next.items.push_back(item); } } @@ -295,14 +295,14 @@ public: Import::Item item; item.balance = gossipItem.balance; item.consumer = newInboundEndpoint(gossipItem.address); - item.consumer.entry().remote_balance += item.balance; + item.consumer.entry().remoteBalance += item.balance; next.items.push_back(item); } Import& prev(resultIt->second); for (auto& item : prev.items) { - item.consumer.entry().remote_balance -= item.balance; + item.consumer.entry().remoteBalance -= item.balance; } std::swap(next, prev); @@ -345,7 +345,7 @@ public: for (auto itemIter(import.items.begin()); itemIter != import.items.end(); ++itemIter) { - itemIter->consumer.entry().remote_balance -= itemIter->balance; + itemIter->consumer.entry().remoteBalance -= itemIter->balance; } iter = importTable_.erase(iter); @@ -520,8 +520,8 @@ public: item["count"] = entry.refcount; item["name"] = entry.toString(); item["balance"] = entry.balance(now); - if (entry.remote_balance != 0) - item["remote_balance"] = entry.remote_balance; + if (entry.remoteBalance != 0) + item["remote_balance"] = entry.remoteBalance; } } diff --git a/include/xrpl/server/Handoff.h b/include/xrpl/server/Handoff.h index eba8c6c4de..b80a9c5745 100644 --- a/include/xrpl/server/Handoff.h +++ b/include/xrpl/server/Handoff.h @@ -21,7 +21,7 @@ struct Handoff bool moved = false; // If response is set, this determines the keep alive - bool keep_alive = false; + bool keepAlive = false; // When set, this will be sent back std::shared_ptr response; diff --git a/include/xrpl/server/Port.h b/include/xrpl/server/Port.h index 9207e0552b..ac9b855cf0 100644 --- a/include/xrpl/server/Port.h +++ b/include/xrpl/server/Port.h @@ -30,19 +30,19 @@ struct Port boost::asio::ip::address ip; std::uint16_t port = 0; std::set protocol; - std::vector admin_nets_v4; - std::vector admin_nets_v6; - std::vector secure_gateway_nets_v4; - std::vector secure_gateway_nets_v6; + std::vector adminNetsV4; + std::vector adminNetsV6; + std::vector secureGatewayNetsV4; + std::vector secureGatewayNetsV6; std::string user; std::string password; - std::string admin_user; - std::string admin_password; - std::string ssl_key; - std::string ssl_cert; - std::string ssl_chain; - std::string ssl_ciphers; - boost::beast::websocket::permessage_deflate pmd_options; + std::string adminUser; + std::string adminPassword; + std::string sslKey; + std::string sslCert; + std::string sslChain; + std::string sslCiphers; + boost::beast::websocket::permessage_deflate pmdOptions; std::shared_ptr context; // How many incoming connections are allowed on this @@ -50,7 +50,7 @@ struct Port int limit = 0; // Websocket disconnects if send queue exceeds this limit - std::uint16_t ws_queue_limit{}; + std::uint16_t wsQueueLimit{}; // Returns `true` if any websocket protocols are specified [[nodiscard]] bool @@ -78,22 +78,22 @@ struct ParsedPort std::set protocol; std::string user; std::string password; - std::string admin_user; - std::string admin_password; - std::string ssl_key; - std::string ssl_cert; - std::string ssl_chain; - std::string ssl_ciphers; - boost::beast::websocket::permessage_deflate pmd_options; + std::string adminUser; + std::string adminPassword; + std::string sslKey; + std::string sslCert; + std::string sslChain; + std::string sslCiphers; + boost::beast::websocket::permessage_deflate pmdOptions; int limit = 0; - std::uint16_t ws_queue_limit{}; + std::uint16_t wsQueueLimit{}; std::optional ip; std::optional port; - std::vector admin_nets_v4; - std::vector admin_nets_v6; - std::vector secure_gateway_nets_v4; - std::vector secure_gateway_nets_v6; + std::vector adminNetsV4; + std::vector adminNetsV6; + std::vector secureGatewayNetsV4; + std::vector secureGatewayNetsV6; }; void diff --git a/include/xrpl/server/detail/BaseHTTPPeer.h b/include/xrpl/server/detail/BaseHTTPPeer.h index 04ec4862c6..d26817bc45 100644 --- a/include/xrpl/server/detail/BaseHTTPPeer.h +++ b/include/xrpl/server/detail/BaseHTTPPeer.h @@ -58,13 +58,13 @@ protected: Handler& handler_; boost::asio::executor_work_guard work_; boost::asio::strand strand_; - endpoint_type remote_address_; + endpoint_type remoteAddress_; beast::Journal const journal_; std::string id_; std::size_t nid_; - boost::asio::streambuf read_buf_; + boost::asio::streambuf readBuf_; http_request_type message_; std::vector wq_; std::vector wq2_; @@ -73,9 +73,9 @@ protected: bool complete_ = false; boost::system::error_code ec_; - int request_count_ = 0; - std::size_t bytes_in_ = 0; - std::size_t bytes_out_ = 0; + int requestCount_ = 0; + std::size_t bytesIn_ = 0; + std::size_t bytesOut_ = 0; //-------------------------------------------------------------------------- @@ -151,7 +151,7 @@ protected: beast::IP::Endpoint remoteAddress() override { - return beast::IPAddressConversion::fromAsio(remote_address_); + return beast::IPAddressConversion::fromAsio(remoteAddress_); } http_request_type& @@ -191,23 +191,23 @@ BaseHTTPPeer::BaseHTTPPeer( , handler_(handler) , work_(boost::asio::make_work_guard(executor)) , strand_(boost::asio::make_strand(executor)) - , remote_address_(std::move(remoteAddress)) + , remoteAddress_(std::move(remoteAddress)) , journal_(journal) { - read_buf_.commit( - boost::asio::buffer_copy(read_buf_.prepare(boost::asio::buffer_size(buffers)), buffers)); + readBuf_.commit( + boost::asio::buffer_copy(readBuf_.prepare(boost::asio::buffer_size(buffers)), buffers)); static std::atomic kSid; nid_ = ++kSid; id_ = std::string("#") + std::to_string(nid_) + " "; - JLOG(journal_.trace()) << id_ << "accept: " << remote_address_.address(); + JLOG(journal_.trace()) << id_ << "accept: " << remoteAddress_.address(); } template BaseHTTPPeer::~BaseHTTPPeer() { handler_.onClose(session(), ec_); - JLOG(journal_.trace()) << id_ << "destroyed: " << request_count_ - << ((request_count_ == 1) ? " request" : " requests"); + JLOG(journal_.trace()) << id_ << "destroyed: " << requestCount_ + << ((requestCount_ == 1) ? " request" : " requests"); } template @@ -245,7 +245,7 @@ BaseHTTPPeer::startTimer() boost::beast::get_lowest_layer(impl().stream_) .expires_after( std::chrono::seconds( - remote_address_.address().is_loopback() ? kTimeoutSecondsLocal : kTimeoutSeconds)); + remoteAddress_.address().is_loopback() ? kTimeoutSecondsLocal : kTimeoutSeconds)); } // Convenience for discarding the error code @@ -274,7 +274,7 @@ BaseHTTPPeer::doRead(yield_context doYield) complete_ = false; error_code ec; startTimer(); - boost::beast::http::async_read(impl().stream_, read_buf_, message_, doYield[ec]); + boost::beast::http::async_read(impl().stream_, readBuf_, message_, doYield[ec]); cancelTimer(); if (ec == boost::beast::http::error::end_of_stream) return doClose(); @@ -296,7 +296,7 @@ BaseHTTPPeer::onWrite(error_code const& ec, std::size_t bytesTran return onTimer(); if (ec) return fail(ec, "write"); - bytes_out_ += bytesTransferred; + bytesOut_ += bytesTransferred; { std::scoped_lock const lock(mutex_); wq2_.clear(); diff --git a/include/xrpl/server/detail/BasePeer.h b/include/xrpl/server/detail/BasePeer.h index 3705ef448e..edde28981c 100644 --- a/include/xrpl/server/detail/BasePeer.h +++ b/include/xrpl/server/detail/BasePeer.h @@ -27,7 +27,7 @@ protected: Port const& port_; Handler& handler_; - endpoint_type remote_address_; + endpoint_type remoteAddress_; beast::WrappedSink sink_; beast::Journal const j_; @@ -65,7 +65,7 @@ BasePeer::BasePeer( beast::Journal journal) : port_(port) , handler_(handler) - , remote_address_(std::move(remoteAddress)) + , remoteAddress_(std::move(remoteAddress)) , sink_( journal.sink(), [] { diff --git a/include/xrpl/server/detail/BaseWSPeer.h b/include/xrpl/server/detail/BaseWSPeer.h index 181cf41f81..13225dcba1 100644 --- a/include/xrpl/server/detail/BaseWSPeer.h +++ b/include/xrpl/server/detail/BaseWSPeer.h @@ -42,15 +42,15 @@ private: /// 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 do_close_ = false; + bool doClose_ = false; boost::beast::websocket::close_reason cr_; waitable_timer timer_; - bool close_on_timer_ = false; - bool ping_active_ = false; + bool closeOnTimer_ = false; + bool pingActive_ = false; boost::beast::websocket::ping_data payload_; error_code ec_; std::function - control_callback_; + controlCallback_; public: template @@ -85,7 +85,7 @@ public: [[nodiscard]] boost::asio::ip::tcp::endpoint const& remoteEndpoint() const override { - return this->remote_address_; + return this->remoteAddress_; } void @@ -173,14 +173,14 @@ BaseWSPeer::run() { if (!strand_.running_in_this_thread()) return post(strand_, std::bind(&BaseWSPeer::run, impl().shared_from_this())); - impl().ws_.set_option(port().pmd_options); + impl().ws_.set_option(port().pmdOptions); // Must manage the control callback memory outside of the `control_callback` // function - control_callback_ = + controlCallback_ = std::bind(&BaseWSPeer::onPingPong, this, std::placeholders::_1, std::placeholders::_2); - impl().ws_.control_callback(control_callback_); + impl().ws_.control_callback(controlCallback_); startTimer(); - close_on_timer_ = true; + closeOnTimer_ = true; impl().ws_.set_option(boost::beast::websocket::stream_base::decorator([](auto& res) { res.set(boost::beast::http::field::server, BuildInfo::getFullVersionString()); })); @@ -198,9 +198,9 @@ BaseWSPeer::send(std::shared_ptr w) { if (!strand_.running_in_this_thread()) return post(strand_, std::bind(&BaseWSPeer::send, impl().shared_from_this(), std::move(w))); - if (do_close_) + if (doClose_) return; - if (wq_.size() > port().ws_queue_limit) + if (wq_.size() > port().wsQueueLimit) { cr_.code = safeCast(boost::beast::websocket::close_code::policy_error); cr_.reason = "Policy error: client is too slow."; @@ -227,9 +227,9 @@ BaseWSPeer::close(boost::beast::websocket::close_reason const& re { if (!strand_.running_in_this_thread()) return post(strand_, [self = impl().shared_from_this(), reason] { self->close(reason); }); - if (do_close_) + if (doClose_) return; - do_close_ = true; + doClose_ = true; if (wq_.empty()) { impl().ws_.async_close( @@ -260,7 +260,7 @@ BaseWSPeer::onWsHandshake(error_code const& ec) { if (ec) return fail(ec, "on_ws_handshake"); - close_on_timer_ = false; + closeOnTimer_ = false; doRead(); } @@ -313,7 +313,7 @@ BaseWSPeer::onWriteFin(error_code const& ec) if (ec) return fail(ec, "write_fin"); wq_.pop_front(); - if (do_close_) + if (doClose_) { impl().ws_.async_close( cr_, @@ -409,7 +409,7 @@ BaseWSPeer::onPing(error_code const& ec) { if (ec == boost::asio::error::operation_aborted) return; - ping_active_ = false; + pingActive_ = false; if (!ec) return; fail(ec, "on_ping"); @@ -426,7 +426,7 @@ BaseWSPeer::onPingPong( boost::beast::string_view const p(payload_.begin()); if (payload == p) { - close_on_timer_ = false; + closeOnTimer_ = false; JLOG(this->j_.trace()) << "got matching pong"; } else @@ -444,11 +444,11 @@ BaseWSPeer::onTimer(error_code ec) return; if (!ec) { - if (!close_on_timer_ || !ping_active_) + if (!closeOnTimer_ || !pingActive_) { startTimer(); - close_on_timer_ = true; - ping_active_ = true; + closeOnTimer_ = true; + pingActive_ = true; // cryptographic is probably overkill.. beast::rngfill(payload_.begin(), payload_.size(), cryptoPrng()); impl().ws_.async_ping( diff --git a/include/xrpl/server/detail/Door.h b/include/xrpl/server/detail/Door.h index d3fff8b476..e273ca791f 100644 --- a/include/xrpl/server/detail/Door.h +++ b/include/xrpl/server/detail/Door.h @@ -61,7 +61,7 @@ private: boost::asio::io_context& ioc_; stream_type stream_; socket_type& socket_; - endpoint_type remote_address_; + endpoint_type remoteAddress_; boost::asio::strand strand_; beast::Journal const j_; @@ -97,8 +97,8 @@ private: (port_.protocol.count("ws2") != 0u)}; static constexpr std::chrono::milliseconds kInitialAcceptDelay{50}; static constexpr std::chrono::milliseconds kMaxAcceptDelay{2000}; - std::chrono::milliseconds accept_delay_{kInitialAcceptDelay}; - boost::asio::steady_timer backoff_timer_; + std::chrono::milliseconds acceptDelay_{kInitialAcceptDelay}; + boost::asio::steady_timer backoffTimer_; static constexpr double kFreeFdThreshold = 0.70; struct FDStats @@ -164,7 +164,7 @@ Door::Detector::Detector( , ioc_(ioc) , stream_(std::move(stream)) , socket_(stream_.socket()) - , remote_address_(std::move(remoteAddress)) + , remoteAddress_(std::move(remoteAddress)) , strand_(boost::asio::make_strand(ioc_)) , j_(j) { @@ -199,18 +199,18 @@ Door::Detector::doDetect(boost::asio::yield_context doYield) if (ssl) { if (auto sp = ios().template emplace>( - port_, handler_, ioc_, j_, remote_address_, buf.data(), std::move(stream_))) + port_, handler_, ioc_, j_, remoteAddress_, buf.data(), std::move(stream_))) sp->run(); return; } if (auto sp = ios().template emplace>( - port_, handler_, ioc_, j_, remote_address_, buf.data(), std::move(stream_))) + port_, handler_, ioc_, j_, remoteAddress_, buf.data(), std::move(stream_))) sp->run(); return; } if (ec != boost::asio::error::operation_aborted) { - JLOG(j_.trace()) << "Error detecting ssl: " << ec.message() << " from " << remote_address_; + JLOG(j_.trace()) << "Error detecting ssl: " << ec.message() << " from " << remoteAddress_; } } @@ -279,7 +279,7 @@ Door::Door( , ioc_(ioContext) , acceptor_(ioContext) , strand_(boost::asio::make_strand(ioContext)) - , backoff_timer_(ioContext) + , backoffTimer_(ioContext) { reOpen(); } @@ -302,7 +302,7 @@ Door::close() return boost::asio::post( strand_, std::bind(&Door::close, this->shared_from_this())); } - backoff_timer_.cancel(); + backoffTimer_.cancel(); error_code ec; acceptor_.close(ec); } @@ -338,11 +338,11 @@ Door::doAccept(boost::asio::yield_context doYield) { if (shouldThrottleForFds()) { - backoff_timer_.expires_after(accept_delay_); + backoffTimer_.expires_after(acceptDelay_); boost::system::error_code tec; - backoff_timer_.async_wait(doYield[tec]); - accept_delay_ = std::min(accept_delay_ * 2, kMaxAcceptDelay); - JLOG(j_.warn()) << "Throttling do_accept for " << accept_delay_.count() << "ms."; + backoffTimer_.async_wait(doYield[tec]); + acceptDelay_ = std::min(acceptDelay_ * 2, kMaxAcceptDelay); + JLOG(j_.warn()) << "Throttling do_accept for " << acceptDelay_.count() << "ms."; continue; } @@ -360,13 +360,13 @@ Door::doAccept(boost::asio::yield_context doYield) ec == boost::asio::error::no_buffer_space) { JLOG(j_.warn()) << "accept: Too many open files. Pausing for " - << accept_delay_.count() << "ms."; + << acceptDelay_.count() << "ms."; - backoff_timer_.expires_after(accept_delay_); + backoffTimer_.expires_after(acceptDelay_); boost::system::error_code tec; - backoff_timer_.async_wait(doYield[tec]); + backoffTimer_.async_wait(doYield[tec]); - accept_delay_ = std::min(accept_delay_ * 2, kMaxAcceptDelay); + acceptDelay_ = std::min(acceptDelay_ * 2, kMaxAcceptDelay); } else { @@ -375,7 +375,7 @@ Door::doAccept(boost::asio::yield_context doYield) continue; } - accept_delay_ = kInitialAcceptDelay; + acceptDelay_ = kInitialAcceptDelay; if (ssl_ && plain_) { diff --git a/include/xrpl/server/detail/PlainHTTPPeer.h b/include/xrpl/server/detail/PlainHTTPPeer.h index 40f7fe2571..791ab91acf 100644 --- a/include/xrpl/server/detail/PlainHTTPPeer.h +++ b/include/xrpl/server/detail/PlainHTTPPeer.h @@ -82,7 +82,7 @@ template void PlainHTTPPeer::run() { - if (!this->handler_.onAccept(this->session(), this->remote_address_)) + if (!this->handler_.onAccept(this->session(), this->remoteAddress_)) { util::spawn(this->strand_, std::bind(&PlainHTTPPeer::doClose, this->shared_from_this())); return; @@ -103,7 +103,7 @@ PlainHTTPPeer::websocketUpgrade() auto ws = this->ios().template emplace>( this->port_, this->handler_, - this->remote_address_, + this->remoteAddress_, std::move(this->message_), std::move(stream_), this->journal_); @@ -114,20 +114,20 @@ template void PlainHTTPPeer::doRequest() { - ++this->request_count_; + ++this->requestCount_; auto const what = - this->handler_.onHandoff(this->session(), std::move(this->message_), this->remote_address_); + this->handler_.onHandoff(this->session(), std::move(this->message_), this->remoteAddress_); if (what.moved) return; boost::system::error_code ec; if (what.response) { // half-close on Connection: close - if (!what.keep_alive) + if (!what.keepAlive) socket_.shutdown(socket_type::shutdown_receive, ec); if (ec) return this->fail(ec, "request"); - return this->write(what.response, what.keep_alive); + return this->write(what.response, what.keepAlive); } // Perform half-close when Connection: close and not SSL diff --git a/include/xrpl/server/detail/SSLHTTPPeer.h b/include/xrpl/server/detail/SSLHTTPPeer.h index 2eec8ed60f..f3b047150f 100644 --- a/include/xrpl/server/detail/SSLHTTPPeer.h +++ b/include/xrpl/server/detail/SSLHTTPPeer.h @@ -26,7 +26,7 @@ private: using yield_context = boost::asio::yield_context; using error_code = boost::system::error_code; - std::unique_ptr stream_ptr_; + std::unique_ptr streamPtr_; stream_type& stream_; socket_type& socket_; @@ -80,8 +80,8 @@ SSLHTTPPeer::SSLHTTPPeer( journal, remoteAddress, buffers) - , stream_ptr_(std::make_unique(middle_type(std::move(stream)), *port.context)) - , stream_(*stream_ptr_) + , streamPtr_(std::make_unique(middle_type(std::move(stream)), *port.context)) + , stream_(*streamPtr_) , socket_(stream_.next_layer().socket()) { } @@ -91,7 +91,7 @@ template void SSLHTTPPeer::run() { - if (!this->handler_.onAccept(this->session(), this->remote_address_)) + if (!this->handler_.onAccept(this->session(), this->remoteAddress_)) { util::spawn(this->strand_, std::bind(&SSLHTTPPeer::doClose, this->shared_from_this())); return; @@ -110,9 +110,9 @@ SSLHTTPPeer::websocketUpgrade() auto ws = this->ios().template emplace>( this->port_, this->handler_, - this->remote_address_, + this->remoteAddress_, std::move(this->message_), - std::move(this->stream_ptr_), + std::move(this->streamPtr_), this->journal_); return ws; } @@ -124,8 +124,8 @@ SSLHTTPPeer::doHandshake(yield_context doYield) boost::system::error_code ec; stream_.set_verify_mode(boost::asio::ssl::verify_none); this->startTimer(); - this->read_buf_.consume( - stream_.async_handshake(stream_type::server, this->read_buf_.data(), doYield[ec])); + this->readBuf_.consume( + stream_.async_handshake(stream_type::server, this->readBuf_.data(), doYield[ec])); this->cancelTimer(); if (ec == boost::beast::error::timeout) return this->onTimer(); @@ -148,13 +148,13 @@ template void SSLHTTPPeer::doRequest() { - ++this->request_count_; + ++this->requestCount_; auto const what = this->handler_.onHandoff( - this->session(), std::move(stream_ptr_), std::move(this->message_), this->remote_address_); + this->session(), std::move(streamPtr_), std::move(this->message_), this->remoteAddress_); if (what.moved) return; if (what.response) - return this->write(what.response, what.keep_alive); + return this->write(what.response, what.keepAlive); // legacy this->handler_.onRequest(this->session()); } diff --git a/include/xrpl/server/detail/SSLWSPeer.h b/include/xrpl/server/detail/SSLWSPeer.h index 19b77ea932..cb03d5a796 100644 --- a/include/xrpl/server/detail/SSLWSPeer.h +++ b/include/xrpl/server/detail/SSLWSPeer.h @@ -28,7 +28,7 @@ class SSLWSPeer : public BaseWSPeer>, using stream_type = boost::beast::ssl_stream; using waitable_timer = boost::asio::basic_waitable_timer; - std::unique_ptr stream_ptr_; + std::unique_ptr streamPtr_; boost::beast::websocket::stream ws_; public: @@ -61,8 +61,8 @@ SSLWSPeer::SSLWSPeer( remoteEndpoint, std::move(request), journal) - , stream_ptr_(std::move(streamPtr)) - , ws_(*stream_ptr_) + , streamPtr_(std::move(streamPtr)) + , ws_(*streamPtr_) { } diff --git a/include/xrpl/server/detail/ServerImpl.h b/include/xrpl/server/detail/ServerImpl.h index df9a2dd3e8..48ffa26cfe 100644 --- a/include/xrpl/server/detail/ServerImpl.h +++ b/include/xrpl/server/detail/ServerImpl.h @@ -66,7 +66,7 @@ private: Handler& handler_; beast::Journal const j_; - boost::asio::io_context& io_context_; + boost::asio::io_context& ioContext_; boost::asio::strand strand_; std::optional> work_; @@ -104,7 +104,7 @@ public: boost::asio::io_context& getIoContext() { - return io_context_; + return ioContext_; } bool @@ -122,9 +122,9 @@ ServerImpl::ServerImpl( beast::Journal journal) : handler_(handler) , j_(journal) - , io_context_(ioContext) - , strand_(boost::asio::make_strand(io_context_)) - , work_(std::in_place, boost::asio::make_work_guard(io_context_)) + , ioContext_(ioContext) + , strand_(boost::asio::make_strand(ioContext_)) + , work_(std::in_place, boost::asio::make_work_guard(ioContext_)) { } @@ -150,7 +150,7 @@ ServerImpl::ports(std::vector const& ports) { ports_.push_back(port); auto& internalPort = ports_.back(); - if (auto sp = ios_.emplace>(handler_, io_context_, internalPort, j_)) + if (auto sp = ios_.emplace>(handler_, ioContext_, internalPort, j_)) { list_.push_back(sp); diff --git a/src/libxrpl/basics/ResolverAsio.cpp b/src/libxrpl/basics/ResolverAsio.cpp index 4a5ceb3d8d..ddf006b681 100644 --- a/src/libxrpl/basics/ResolverAsio.cpp +++ b/src/libxrpl/basics/ResolverAsio.cpp @@ -108,7 +108,7 @@ public: beast::Journal journal; - boost::asio::io_context& io_context; + boost::asio::io_context& ioContext; boost::asio::strand strand; boost::asio::ip::tcp::resolver resolver; @@ -116,7 +116,7 @@ public: std::mutex mut; bool asyncHandlersCompleted{true}; - std::atomic stop_called; + std::atomic stopCalled; std::atomic stopped; // Represents a unit of work for the resolver to do @@ -138,10 +138,10 @@ public: ResolverAsioImpl(boost::asio::io_context& ioContext, beast::Journal journal) : journal(journal) - , io_context(ioContext) + , ioContext(ioContext) , strand(boost::asio::make_strand(ioContext)) , resolver(ioContext) - , stop_called(false) + , stopCalled(false) , stopped(true) { } @@ -172,7 +172,7 @@ public: start() override { XRPL_ASSERT(stopped == true, "xrpl::ResolverAsioImpl::start : stopped"); - XRPL_ASSERT(stop_called == false, "xrpl::ResolverAsioImpl::start : not stopping"); + XRPL_ASSERT(stopCalled == false, "xrpl::ResolverAsioImpl::start : not stopping"); if (stopped.exchange(false)) { @@ -187,10 +187,10 @@ public: void stopAsync() override { - if (!stop_called.exchange(true)) + if (!stopCalled.exchange(true)) { boost::asio::dispatch( - io_context, + ioContext, boost::asio::bind_executor( strand, std::bind(&ResolverAsioImpl::doStop, this, CompletionCounter(this)))); @@ -213,13 +213,13 @@ public: void resolve(std::vector const& names, HandlerType const& handler) override { - XRPL_ASSERT(stop_called == false, "xrpl::ResolverAsioImpl::resolve : not stopping"); + XRPL_ASSERT(stopCalled == false, "xrpl::ResolverAsioImpl::resolve : not stopping"); XRPL_ASSERT(!names.empty(), "xrpl::ResolverAsioImpl::resolve : names non-empty"); // TODO NIKB use rvalue references to construct and move // reducing cost. boost::asio::dispatch( - io_context, + ioContext, boost::asio::bind_executor( strand, std::bind( @@ -231,7 +231,7 @@ public: void doStop(CompletionCounter) { - XRPL_ASSERT(stop_called == true, "xrpl::ResolverAsioImpl::doStop : stopping"); + XRPL_ASSERT(stopCalled == true, "xrpl::ResolverAsioImpl::doStop : stopping"); if (!stopped.exchange(true)) { @@ -270,7 +270,7 @@ public: handler(name, addresses); boost::asio::post( - io_context, + ioContext, boost::asio::bind_executor( strand, std::bind(&ResolverAsioImpl::doWork, this, CompletionCounter(this)))); } @@ -324,7 +324,7 @@ public: void doWork(CompletionCounter) { - if (stop_called) + if (stopCalled) return; // We don't have any work to do at this time @@ -346,7 +346,7 @@ public: JLOG(journal.error()) << "Unable to parse '" << name << "'"; boost::asio::post( - io_context, + ioContext, boost::asio::bind_executor( strand, std::bind(&ResolverAsioImpl::doWork, this, CompletionCounter(this)))); @@ -371,7 +371,7 @@ public: { XRPL_ASSERT(!names.empty(), "xrpl::ResolverAsioImpl::doResolve : names non-empty"); - if (!stop_called) + if (!stopCalled) { work.emplace_back(names, handler); @@ -381,7 +381,7 @@ public: if (!work.empty()) { boost::asio::post( - io_context, + ioContext, boost::asio::bind_executor( strand, std::bind(&ResolverAsioImpl::doWork, this, CompletionCounter(this)))); diff --git a/src/libxrpl/beast/insight/StatsDCollector.cpp b/src/libxrpl/beast/insight/StatsDCollector.cpp index 7d460d54ef..88f52c26de 100644 --- a/src/libxrpl/beast/insight/StatsDCollector.cpp +++ b/src/libxrpl/beast/insight/StatsDCollector.cpp @@ -164,7 +164,7 @@ public: private: std::shared_ptr impl_; std::string name_; - GaugeImpl::value_type last_value_{0}; + GaugeImpl::value_type lastValue_{0}; GaugeImpl::value_type value_{0}; bool dirty_{false}; }; @@ -209,7 +209,7 @@ private: Journal journal_; IP::Endpoint address_; std::string prefix_; - boost::asio::io_context io_context_; + boost::asio::io_context ioContext_; std::optional> work_; boost::asio::strand strand_; boost::asio::basic_waitable_timer timer_; @@ -232,10 +232,10 @@ public: : journal_(journal) , address_(std::move(address)) , prefix_(std::move(prefix)) - , work_(boost::asio::make_work_guard(io_context_)) - , strand_(boost::asio::make_strand(io_context_)) - , timer_(io_context_) - , socket_(io_context_) + , work_(boost::asio::make_work_guard(ioContext_)) + , strand_(boost::asio::make_strand(ioContext_)) + , timer_(ioContext_) + , socket_(ioContext_) , thread_(&StatsDCollectorImp::run, this) { } @@ -306,7 +306,7 @@ public: boost::asio::io_context& getIoContext() { - return io_context_; + return ioContext_; } std::string const& @@ -325,7 +325,7 @@ public: postBuffer(std::string&& buffer) { boost::asio::dispatch( - io_context_, + ioContext_, boost::asio::bind_executor( strand_, std::bind(&StatsDCollectorImp::doPostBuffer, this, std::move(buffer)))); } @@ -465,14 +465,14 @@ public: setTimer(); - io_context_.run(); + ioContext_.run(); // NOLINTNEXTLINE(bugprone-unused-return-value) socket_.shutdown(boost::asio::ip::udp::socket::shutdown_send, ec); socket_.close(); - io_context_.poll(); + ioContext_.poll(); } }; @@ -628,9 +628,9 @@ StatsDGaugeImpl::doSet(GaugeImpl::value_type value) { value_ = value; - if (value_ != last_value_) + if (value_ != lastValue_) { - last_value_ = value_; + lastValue_ = value_; dirty_ = true; } } diff --git a/src/libxrpl/core/detail/Job.cpp b/src/libxrpl/core/detail/Job.cpp index a1b88864d7..89fe42db05 100644 --- a/src/libxrpl/core/detail/Job.cpp +++ b/src/libxrpl/core/detail/Job.cpp @@ -25,7 +25,7 @@ Job::Job( std::uint64_t index, LoadMonitor& lm, std::function const& job) - : type_(type), jobIndex_(index), job_(job), name_(name), queue_time_(clock_type::now()) + : type_(type), jobIndex_(index), job_(job), name_(name), queueTime_(clock_type::now()) { loadEvent_ = std::make_shared(std::ref(lm), name, false); } @@ -39,7 +39,7 @@ Job::getType() const Job::clock_type::time_point const& Job::queueTime() const { - return queue_time_; + return queueTime_; } void diff --git a/src/libxrpl/core/detail/JobQueue.cpp b/src/libxrpl/core/detail/JobQueue.cpp index 50f95dcdbf..5c07bf68cc 100644 --- a/src/libxrpl/core/detail/JobQueue.cpp +++ b/src/libxrpl/core/detail/JobQueue.cpp @@ -36,7 +36,7 @@ JobQueue::JobQueue( JLOG(journal_.info()) << "Using " << threadCount << " threads"; hook_ = collector_->makeHook(std::bind(&JobQueue::collect, this)); - job_count_ = collector_->makeGauge("job_count"); + jobCount_ = collector_->makeGauge("job_count"); { std::scoped_lock const lock(mutex_); @@ -66,7 +66,7 @@ void JobQueue::collect() { std::scoped_lock const lock(mutex_); - job_count_ = jobSet_.size(); + jobCount_ = jobSet_.size(); } bool diff --git a/src/libxrpl/ledger/BookDirs.cpp b/src/libxrpl/ledger/BookDirs.cpp index f3e41e4f58..099fec99e1 100644 --- a/src/libxrpl/ledger/BookDirs.cpp +++ b/src/libxrpl/ledger/BookDirs.cpp @@ -14,8 +14,8 @@ namespace xrpl { BookDirs::BookDirs(ReadView const& view, Book const& book) : view_(&view) , root_(keylet::page(getBookBase(book)).key) - , next_quality_(getQualityNext(root_)) - , key_(view_->succ(root_, next_quality_).value_or(beast::kZero)) + , nextQuality_(getQualityNext(root_)) + , key_(view_->succ(root_, nextQuality_).value_or(beast::kZero)) { XRPL_ASSERT(root_ != beast::kZero, "xrpl::BookDirs::BookDirs : nonzero root"); if (key_ != beast::kZero) @@ -35,7 +35,7 @@ BookDirs::begin() const -> BookDirs::const_iterator auto it = BookDirs::const_iterator(*view_, root_, key_); if (key_ != beast::kZero) { - it.next_quality_ = next_quality_; + it.nextQuality_ = nextQuality_; it.sle_ = sle_; it.entry_ = entry_; it.index_ = index_; @@ -59,7 +59,7 @@ BookDirs::const_iterator::operator==(BookDirs::const_iterator const& other) cons view_ == other.view_ && root_ == other.root_, "xrpl::BookDirs::const_iterator::operator== : views and roots are " "matching"); - return entry_ == other.entry_ && cur_key_ == other.cur_key_ && index_ == other.index_; + return entry_ == other.entry_ && curKey_ == other.curKey_ && index_ == other.index_; } BookDirs::const_iterator::reference @@ -78,18 +78,18 @@ BookDirs::const_iterator::operator++() using beast::kZero; XRPL_ASSERT(index_ != kZero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index"); - if (!cdirNext(*view_, cur_key_, sle_, entry_, index_)) + if (!cdirNext(*view_, curKey_, sle_, entry_, index_)) { if (index_ == 0) - cur_key_ = view_->succ(++cur_key_, next_quality_).value_or(kZero); + curKey_ = view_->succ(++curKey_, nextQuality_).value_or(kZero); - if (index_ != 0 || cur_key_ == kZero) + if (index_ != 0 || curKey_ == kZero) { - cur_key_ = key_; + curKey_ = key_; entry_ = 0; index_ = kZero; } - else if (!cdirFirst(*view_, cur_key_, sle_, entry_, index_)) + else if (!cdirFirst(*view_, curKey_, sle_, entry_, index_)) { // LCOV_EXCL_START UNREACHABLE("xrpl::BookDirs::const_iterator::operator++ : directory is empty"); diff --git a/src/libxrpl/ledger/OpenView.cpp b/src/libxrpl/ledger/OpenView.cpp index c5bda2d0c8..40b411fcc0 100644 --- a/src/libxrpl/ledger/OpenView.cpp +++ b/src/libxrpl/ledger/OpenView.cpp @@ -78,9 +78,9 @@ public: OpenView::OpenView(OpenView const& rhs) : ReadView(rhs) , TxsRawView(rhs) - , monotonic_resource_{std::make_unique( + , monotonicResource_{std::make_unique( kInitialBufferSize)} - , txs_{rhs.txs_, monotonic_resource_.get()} + , txs_{rhs.txs_, monotonicResource_.get()} , rules_{rhs.rules_} , header_{rhs.header_} , base_{rhs.base_} @@ -89,9 +89,9 @@ OpenView::OpenView(OpenView const& rhs) , open_{rhs.open_} {}; OpenView::OpenView(OpenLedgerT, ReadView const* base, Rules rules, std::shared_ptr hold) - : monotonic_resource_{ + : monotonicResource_{ std::make_unique(kInitialBufferSize)} - , txs_{monotonic_resource_.get()} + , txs_{monotonicResource_.get()} , rules_(std::move(rules)) , header_(base->header()) , base_(base) @@ -105,9 +105,9 @@ OpenView::OpenView(OpenLedgerT, ReadView const* base, Rules rules, std::shared_p } OpenView::OpenView(ReadView const* base, std::shared_ptr hold) - : monotonic_resource_{ + : monotonicResource_{ std::make_unique(kInitialBufferSize)} - , txs_{monotonic_resource_.get()} + , txs_{monotonicResource_.get()} , rules_(base->rules()) , header_(base->header()) , base_(base) diff --git a/src/libxrpl/protocol/ErrorCodes.cpp b/src/libxrpl/protocol/ErrorCodes.cpp index 25c5fbe033..87761ce13e 100644 --- a/src/libxrpl/protocol/ErrorCodes.cpp +++ b/src/libxrpl/protocol/ErrorCodes.cpp @@ -212,7 +212,7 @@ containsError(json::Value const& json) int errorCodeHttpStatus(ErrorCodeI code) { - return getErrorInfo(code).http_status; + return getErrorInfo(code).httpStatus; } } // namespace RPC diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 8636c99284..2777981fd7 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -69,8 +69,8 @@ getTxFormat(TxType type) STTx::STTx(STObject&& object) : STObject(std::move(object)) { - tx_type_ = safeCast(getFieldU16(sfTransactionType)); - applyTemplate(getTxFormat(tx_type_)->getSOTemplate()); // may throw + txType_ = safeCast(getFieldU16(sfTransactionType)); + applyTemplate(getTxFormat(txType_)->getSOTemplate()); // may throw tid_ = getHash(HashPrefix::TransactionId); } @@ -84,9 +84,9 @@ STTx::STTx(SerialIter& sit) : STObject(sfTransaction) if (set(sit)) Throw("Transaction contains an object terminator"); - tx_type_ = safeCast(getFieldU16(sfTransactionType)); + txType_ = safeCast(getFieldU16(sfTransactionType)); - applyTemplate(getTxFormat(tx_type_)->getSOTemplate()); // May throw + applyTemplate(getTxFormat(txType_)->getSOTemplate()); // May throw tid_ = getHash(HashPrefix::TransactionId); } @@ -99,9 +99,9 @@ STTx::STTx(TxType type, std::function assembler) : STObject(sfT assembler(*this); - tx_type_ = safeCast(getFieldU16(sfTransactionType)); + txType_ = safeCast(getFieldU16(sfTransactionType)); - if (tx_type_ != type) + if (txType_ != type) logicError("Transaction type was mutated during assembly"); tid_ = getHash(HashPrefix::TransactionId); @@ -380,7 +380,7 @@ STTx::getMetaSQL( static boost::format const kBfTrans("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)"); std::string rTxn = sqlBlobLiteral(rawTxn.peekData()); - auto format = TxFormats::getInstance().findByType(tx_type_); + auto format = TxFormats::getInstance().findByType(txType_); XRPL_ASSERT(format, "xrpl::STTx::getMetaSQL : non-null type format"); return str( diff --git a/src/libxrpl/server/Port.cpp b/src/libxrpl/server/Port.cpp index 9a6b6dce35..00c10b2b55 100644 --- a/src/libxrpl/server/Port.cpp +++ b/src/libxrpl/server/Port.cpp @@ -44,30 +44,30 @@ operator<<(std::ostream& os, Port const& p) { os << "'" << p.name << "' (ip=" << p.ip << ":" << p.port << ", "; - if (!p.admin_nets_v4.empty() || !p.admin_nets_v6.empty()) + if (!p.adminNetsV4.empty() || !p.adminNetsV6.empty()) { os << "admin nets:"; - for (auto const& net : p.admin_nets_v4) + for (auto const& net : p.adminNetsV4) { os << net.to_string(); os << ", "; } - for (auto const& net : p.admin_nets_v6) + for (auto const& net : p.adminNetsV6) { os << net.to_string(); os << ", "; } } - if (!p.secure_gateway_nets_v4.empty() || !p.secure_gateway_nets_v6.empty()) + if (!p.secureGatewayNetsV4.empty() || !p.secureGatewayNetsV6.empty()) { os << "secure_gateway nets:"; - for (auto const& net : p.secure_gateway_nets_v4) + for (auto const& net : p.secureGatewayNetsV4) { os << net.to_string(); os << ", "; } - for (auto const& net : p.secure_gateway_nets_v6) + for (auto const& net : p.secureGatewayNetsV6) { os << net.to_string(); os << ", "; @@ -265,10 +265,10 @@ parsePort(ParsedPort& port, Section const& section, std::ostream& log) { try { - port.ws_queue_limit = beast::lexicalCastThrow(*optResult); + port.wsQueueLimit = beast::lexicalCastThrow(*optResult); // Queue must be greater than 0 - if (port.ws_queue_limit == 0) + if (port.wsQueueLimit == 0) Throw(); } catch (std::exception const&) @@ -281,32 +281,31 @@ parsePort(ParsedPort& port, Section const& section, std::ostream& log) else { // Default Websocket send queue size limit - port.ws_queue_limit = 100; + port.wsQueueLimit = 100; } } - populate(section, "admin", log, port.admin_nets_v4, port.admin_nets_v6); - populate( - section, "secure_gateway", log, port.secure_gateway_nets_v4, port.secure_gateway_nets_v6); + populate(section, "admin", log, port.adminNetsV4, port.adminNetsV6); + populate(section, "secure_gateway", log, port.secureGatewayNetsV4, port.secureGatewayNetsV6); set(port.user, "user", section); set(port.password, "password", section); - set(port.admin_user, "admin_user", section); - set(port.admin_password, "admin_password", section); - set(port.ssl_key, "ssl_key", section); - set(port.ssl_cert, "ssl_cert", section); - set(port.ssl_chain, "ssl_chain", section); - set(port.ssl_ciphers, "ssl_ciphers", section); + set(port.adminUser, "admin_user", section); + set(port.adminPassword, "admin_password", section); + set(port.sslKey, "ssl_key", section); + set(port.sslCert, "ssl_cert", section); + set(port.sslChain, "ssl_chain", section); + set(port.sslCiphers, "ssl_ciphers", section); - port.pmd_options.server_enable = section.valueOr("permessage_deflate", true); - port.pmd_options.client_max_window_bits = section.valueOr("client_max_window_bits", 15); - port.pmd_options.server_max_window_bits = section.valueOr("server_max_window_bits", 15); - port.pmd_options.client_no_context_takeover = + port.pmdOptions.server_enable = section.valueOr("permessage_deflate", true); + port.pmdOptions.client_max_window_bits = section.valueOr("client_max_window_bits", 15); + port.pmdOptions.server_max_window_bits = section.valueOr("server_max_window_bits", 15); + port.pmdOptions.client_no_context_takeover = section.valueOr("client_no_context_takeover", false); - port.pmd_options.server_no_context_takeover = + port.pmdOptions.server_no_context_takeover = section.valueOr("server_no_context_takeover", false); - port.pmd_options.compLevel = section.valueOr("compress_level", 8); - port.pmd_options.memLevel = section.valueOr("memory_level", 4); + port.pmdOptions.compLevel = section.valueOr("compress_level", 8); + port.pmdOptions.memLevel = section.valueOr("memory_level", 4); } } // namespace xrpl diff --git a/src/test/app/AMMMPT_test.cpp b/src/test/app/AMMMPT_test.cpp index eba388e5fd..cfb8edc60b 100644 --- a/src/test/app/AMMMPT_test.cpp +++ b/src/test/app/AMMMPT_test.cpp @@ -6299,9 +6299,9 @@ private: struct MPTList { - MPTTester const USD; - MPTTester const ETH; - MPTTester const CAN; + MPTTester const usd; + MPTTester const eth; + MPTTester const can; }; auto prep = [&](Env& env, uint16_t gwTransferFee, uint16_t gw1TransferFee) -> MPTList { @@ -6330,9 +6330,9 @@ private: env.close(); return MPTList{ - .USD = std::move(usd), - .ETH = std::move(eth), - .CAN = std::move(can), + .usd = std::move(usd), + .eth = std::move(eth), + .can = std::move(can), }; }; @@ -6357,9 +6357,9 @@ private: { Env env(*this, features); auto mpts = prep(env, rates.first, rates.second); - auto usd = mpts.USD; - auto eth = mpts.ETH; - auto can = mpts.CAN; + auto usd = mpts.usd; + auto eth = mpts.eth; + auto can = mpts.can; std::optional amm; if (i == 0 || i == 2) @@ -6402,9 +6402,9 @@ private: { Env env(*this, features); auto mpts = prep(env, rates.first, rates.second); - auto usd = mpts.USD; - auto eth = mpts.ETH; - auto can = mpts.CAN; + auto usd = mpts.usd; + auto eth = mpts.eth; + auto can = mpts.can; std::optional amm; if (i == 0 || i == 2) { @@ -6447,9 +6447,9 @@ private: { Env env(*this, features); auto mpts = prep(env, rates.first, rates.second); - auto usd = mpts.USD; - auto eth = mpts.ETH; - auto can = mpts.CAN; + auto usd = mpts.usd; + auto eth = mpts.eth; + auto can = mpts.can; std::optional amm; if (i == 0 || i == 2) { @@ -6513,9 +6513,9 @@ private: { Env env(*this, features); auto mpts = prep(env, rates.first, rates.second); - auto usd = mpts.USD; - auto eth = mpts.ETH; - auto can = mpts.CAN; + auto usd = mpts.usd; + auto eth = mpts.eth; + auto can = mpts.can; std::optional amm; if (i == 0 || i == 2) { @@ -6591,9 +6591,9 @@ private: { Env env(*this, features); auto mpts = prep(env, rates.first, rates.second); - auto usd = mpts.USD; - auto eth = mpts.ETH; - auto can = mpts.CAN; + auto usd = mpts.usd; + auto eth = mpts.eth; + auto can = mpts.can; std::optional amm; if (i == 0 || i == 2) @@ -6929,7 +6929,7 @@ private: Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = XRPAmount(1); + cfg->fees.referenceFee = XRPAmount(1); return cfg; }), all); @@ -6983,7 +6983,7 @@ private: Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = XRPAmount(1); + cfg->fees.referenceFee = XRPAmount(1); return cfg; }), all); @@ -7018,7 +7018,7 @@ private: Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = XRPAmount(1); + cfg->fees.referenceFee = XRPAmount(1); return cfg; }), all); @@ -7042,7 +7042,7 @@ private: Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = XRPAmount(1); + cfg->fees.referenceFee = XRPAmount(1); return cfg; }), all); diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index 68ebdbcf60..17f959ae3c 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -5093,7 +5093,7 @@ private: Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = XRPAmount(1); + cfg->fees.referenceFee = XRPAmount(1); return cfg; }), all); @@ -5151,7 +5151,7 @@ private: Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = XRPAmount(1); + cfg->fees.referenceFee = XRPAmount(1); return cfg; }), all); @@ -7090,7 +7090,7 @@ private: Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = XRPAmount(1); + cfg->fees.referenceFee = XRPAmount(1); return cfg; }), features); diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index 4fb97a0b74..22e8322bb5 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -213,18 +213,18 @@ class FeeVote_test : public beast::unit_test::Suite // defaults Section const config; auto setup = setupFeeVote(config); - BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); - BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); - BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT(setup.referenceFee == defaultSetup.referenceFee); + BEAST_EXPECT(setup.accountReserve == defaultSetup.accountReserve); + BEAST_EXPECT(setup.ownerReserve == defaultSetup.ownerReserve); } { Section config; config.append( {"reference_fee = 50", "account_reserve = 1234567", "owner_reserve = 1234"}); auto setup = setupFeeVote(config); - BEAST_EXPECT(setup.reference_fee == 50); - BEAST_EXPECT(setup.account_reserve == 1234567); - BEAST_EXPECT(setup.owner_reserve == 1234); + BEAST_EXPECT(setup.referenceFee == 50); + BEAST_EXPECT(setup.accountReserve == 1234567); + BEAST_EXPECT(setup.ownerReserve == 1234); } { Section config; @@ -232,9 +232,9 @@ class FeeVote_test : public beast::unit_test::Suite {"reference_fee = blah", "account_reserve = yada", "owner_reserve = foo"}); // Illegal values are ignored, and the defaults left unchanged auto setup = setupFeeVote(config); - BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); - BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); - BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT(setup.referenceFee == defaultSetup.referenceFee); + BEAST_EXPECT(setup.accountReserve == defaultSetup.accountReserve); + BEAST_EXPECT(setup.ownerReserve == defaultSetup.ownerReserve); } { Section config; @@ -242,9 +242,9 @@ class FeeVote_test : public beast::unit_test::Suite {"reference_fee = -50", "account_reserve = -1234567", "owner_reserve = -1234"}); // Illegal values are ignored, and the defaults left unchanged auto setup = setupFeeVote(config); - BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); - BEAST_EXPECT(setup.account_reserve == static_cast(-1234567)); - BEAST_EXPECT(setup.owner_reserve == static_cast(-1234)); + BEAST_EXPECT(setup.referenceFee == defaultSetup.referenceFee); + BEAST_EXPECT(setup.accountReserve == static_cast(-1234567)); + BEAST_EXPECT(setup.ownerReserve == static_cast(-1234)); } { auto const big64 = std::to_string( @@ -256,9 +256,9 @@ class FeeVote_test : public beast::unit_test::Suite "owner_reserve = " + big64}); // Illegal values are ignored, and the defaults left unchanged auto setup = setupFeeVote(config); - BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); - BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); - BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT(setup.referenceFee == defaultSetup.referenceFee); + BEAST_EXPECT(setup.accountReserve == defaultSetup.accountReserve); + BEAST_EXPECT(setup.ownerReserve == defaultSetup.ownerReserve); } } @@ -273,7 +273,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -303,7 +303,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -336,7 +336,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -358,7 +358,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -385,7 +385,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -424,7 +424,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -472,7 +472,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -503,7 +503,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -549,7 +549,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -578,9 +578,9 @@ class FeeVote_test : public beast::unit_test::Suite using namespace jtx; FeeSetup setup; - setup.reference_fee = 42; - setup.account_reserve = 1234567; - setup.owner_reserve = 7654321; + setup.referenceFee = 42; + setup.accountReserve = 1234567; + setup.ownerReserve = 7654321; // Test with XRPFees enabled { @@ -590,7 +590,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -609,7 +609,7 @@ class FeeVote_test : public beast::unit_test::Suite feeVote->doValidation(currentFees, ledger->rules(), *val); BEAST_EXPECT(val->isFieldPresent(sfBaseFeeDrops)); - BEAST_EXPECT(val->getFieldAmount(sfBaseFeeDrops) == XRPAmount(setup.reference_fee)); + BEAST_EXPECT(val->getFieldAmount(sfBaseFeeDrops) == XRPAmount(setup.referenceFee)); } // Test with XRPFees disabled (legacy format) @@ -620,7 +620,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -638,7 +638,7 @@ class FeeVote_test : public beast::unit_test::Suite // In legacy mode, should vote using legacy fields BEAST_EXPECT(val->isFieldPresent(sfBaseFee)); - BEAST_EXPECT(val->getFieldU64(sfBaseFee) == setup.reference_fee); + BEAST_EXPECT(val->getFieldU64(sfBaseFee) == setup.referenceFee); } } @@ -650,9 +650,9 @@ class FeeVote_test : public beast::unit_test::Suite using namespace jtx; FeeSetup setup; - setup.reference_fee = 42; - setup.account_reserve = 1234567; - setup.owner_reserve = 7654321; + setup.referenceFee = 42; + setup.accountReserve = 1234567; + setup.ownerReserve = 7654321; Env env(*this, testableAmendments() | featureXRPFees); @@ -665,7 +665,7 @@ class FeeVote_test : public beast::unit_test::Suite auto ledger = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -689,9 +689,9 @@ class FeeVote_test : public beast::unit_test::Suite env.app().getTimeKeeper().now(), pub, sec, calcNodeID(pub), [&](STValidation& v) { v.setFieldU32(sfLedgerSequence, ledger->seq()); // Vote for different fees than current - v.setFieldAmount(sfBaseFeeDrops, XRPAmount{setup.reference_fee}); - v.setFieldAmount(sfReserveBaseDrops, XRPAmount{setup.account_reserve}); - v.setFieldAmount(sfReserveIncrementDrops, XRPAmount{setup.owner_reserve}); + v.setFieldAmount(sfBaseFeeDrops, XRPAmount{setup.referenceFee}); + v.setFieldAmount(sfReserveBaseDrops, XRPAmount{setup.accountReserve}); + v.setFieldAmount(sfReserveIncrementDrops, XRPAmount{setup.ownerReserve}); }); if ((i % 2) != 0) val->setTrusted(); @@ -723,10 +723,10 @@ class FeeVote_test : public beast::unit_test::Suite BEAST_EXPECT(!feeTx.isFieldPresent(sfReferenceFeeUnits)); // Check the values - BEAST_EXPECT(feeTx.getFieldAmount(sfBaseFeeDrops) == XRPAmount{setup.reference_fee}); - BEAST_EXPECT(feeTx.getFieldAmount(sfReserveBaseDrops) == XRPAmount{setup.account_reserve}); + BEAST_EXPECT(feeTx.getFieldAmount(sfBaseFeeDrops) == XRPAmount{setup.referenceFee}); + BEAST_EXPECT(feeTx.getFieldAmount(sfReserveBaseDrops) == XRPAmount{setup.accountReserve}); BEAST_EXPECT( - feeTx.getFieldAmount(sfReserveIncrementDrops) == XRPAmount{setup.owner_reserve}); + feeTx.getFieldAmount(sfReserveIncrementDrops) == XRPAmount{setup.ownerReserve}); } void diff --git a/src/test/app/LedgerHistory_test.cpp b/src/test/app/LedgerHistory_test.cpp index e464962258..3d0e546678 100644 --- a/src/test/app/LedgerHistory_test.cpp +++ b/src/test/app/LedgerHistory_test.cpp @@ -50,7 +50,7 @@ public: return std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); } diff --git a/src/test/app/LedgerLoad_test.cpp b/src/test/app/LedgerLoad_test.cpp index fa2c8f9047..ee3bfe5192 100644 --- a/src/test/app/LedgerLoad_test.cpp +++ b/src/test/app/LedgerLoad_test.cpp @@ -40,9 +40,9 @@ class LedgerLoad_test : public beast::unit_test::Suite StartUpType type, std::optional trapTxHash) { - cfg->START_LEDGER = ledger; - cfg->START_UP = type; - cfg->TRAP_TX_HASH = trapTxHash; + cfg->startLedger = ledger; + cfg->startUp = type; + cfg->trapTxHash = trapTxHash; assert(!dbPath.empty()); cfg->legacy("database_path", dbPath); return cfg; diff --git a/src/test/app/LedgerMaster_test.cpp b/src/test/app/LedgerMaster_test.cpp index ee14a93d07..3cf9b3a9d9 100644 --- a/src/test/app/LedgerMaster_test.cpp +++ b/src/test/app/LedgerMaster_test.cpp @@ -27,9 +27,9 @@ class LedgerMaster_test : public beast::unit_test::Suite { using namespace jtx; return envconfig([&](std::unique_ptr cfg) { - cfg->NETWORK_ID = networkID; + cfg->networkId = networkID; // This test relies on ledger hash so must lock it to fee 10. - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; return cfg; }); } diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index 4b1e4510d6..1978d04fe1 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -1062,7 +1062,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite testcase("config test"); { Config const c; - BEAST_EXPECT(c.LEDGER_REPLAY == false); + BEAST_EXPECT(c.ledgerReplay == false); } { @@ -1072,7 +1072,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite 1 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.LEDGER_REPLAY == true); + BEAST_EXPECT(c.ledgerReplay == true); } { @@ -1082,7 +1082,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite 0 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.LEDGER_REPLAY == false); + BEAST_EXPECT(c.ledgerReplay == false); } } @@ -1101,7 +1101,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite beast::IP::Address const addr = boost::asio::ip::make_address("172.1.1.100"); jtx::Env serverEnv(*this); - serverEnv.app().config().LEDGER_REPLAY = server; + serverEnv.app().config().ledgerReplay = server; auto httpResp = xrpl::makeResponse( true, httpRequest, addr, addr, uint256{1}, 1, {1, 0}, serverEnv.app()); auto const clientResult = peerFeatureEnabled(httpResp, kFeatureLedgerReplay, client); diff --git a/src/test/app/LoadFeeTrack_test.cpp b/src/test/app/LoadFeeTrack_test.cpp index 528e90321b..aba0b35062 100644 --- a/src/test/app/LoadFeeTrack_test.cpp +++ b/src/test/app/LoadFeeTrack_test.cpp @@ -17,7 +17,7 @@ public: { Fees const fees = [&]() { Fees f; - f.base = d.FEES.reference_fee; + f.base = d.fees.referenceFee; f.reserve = 200 * kDropsPerXrp; f.increment = 50 * kDropsPerXrp; return f; @@ -30,7 +30,7 @@ public: { Fees const fees = [&]() { Fees f; - f.base = d.FEES.reference_fee * 10; + f.base = d.fees.referenceFee * 10; f.reserve = 200 * kDropsPerXrp; f.increment = 50 * kDropsPerXrp; return f; @@ -43,7 +43,7 @@ public: { Fees const fees = [&]() { Fees f; - f.base = d.FEES.reference_fee; + f.base = d.fees.referenceFee; f.reserve = 200 * kDropsPerXrp; f.increment = 50 * kDropsPerXrp; return f; diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 0e48db6080..74bdc656df 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -2125,7 +2125,7 @@ class MPToken_test : public beast::unit_test::Suite Account const alice{"alice"}; auto cfg = envconfig(); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env{*this, std::move(cfg), features}; MPTTester mptAlice(env, alice); diff --git a/src/test/app/NetworkID_test.cpp b/src/test/app/NetworkID_test.cpp index a98fbd6b4f..677d8f166d 100644 --- a/src/test/app/NetworkID_test.cpp +++ b/src/test/app/NetworkID_test.cpp @@ -37,7 +37,7 @@ public: { using namespace jtx; return envconfig([&](std::unique_ptr cfg) { - cfg->NETWORK_ID = networkID; + cfg->networkId = networkID; return cfg; }); } diff --git a/src/test/app/PathMPT_test.cpp b/src/test/app/PathMPT_test.cpp index da5597cf05..3ba67b58a6 100644 --- a/src/test/app/PathMPT_test.cpp +++ b/src/test/app/PathMPT_test.cpp @@ -87,9 +87,9 @@ class PathMPT_test : public beast::unit_test::Suite // with the search parameters that the tests were written for. using namespace jtx; return Env(*this, envconfig([](std::unique_ptr cfg) { - cfg->PATH_SEARCH_OLD = 7; - cfg->PATH_SEARCH = 7; - cfg->PATH_SEARCH_MAX = 10; + cfg->pathSearchOld = 7; + cfg->pathSearch = 7; + cfg->pathSearchMax = 10; return cfg; })); } diff --git a/src/test/app/Path_test.cpp b/src/test/app/Path_test.cpp index 1cf8131206..4cfe938798 100644 --- a/src/test/app/Path_test.cpp +++ b/src/test/app/Path_test.cpp @@ -101,9 +101,9 @@ class Path_test : public beast::unit_test::Suite // with the search parameters that the tests were written for. using namespace jtx; return Env(*this, envconfig([](std::unique_ptr cfg) { - cfg->PATH_SEARCH_OLD = 7; - cfg->PATH_SEARCH = 7; - cfg->PATH_SEARCH_MAX = 10; + cfg->pathSearchOld = 7; + cfg->pathSearch = 7; + cfg->pathSearchMax = 10; return cfg; })); } diff --git a/src/test/app/RCLValidations_test.cpp b/src/test/app/RCLValidations_test.cpp index df8e873c75..c3b0ddea9c 100644 --- a/src/test/app/RCLValidations_test.cpp +++ b/src/test/app/RCLValidations_test.cpp @@ -71,7 +71,7 @@ class RCLValidations_test : public beast::unit_test::Suite auto prev = std::make_shared( kCreateGenesis, Rules{config.features}, - config.FEES.toFees(), + config.fees.toFees(), std::vector{}, env.app().getNodeFamily()); history.push_back(prev); @@ -237,7 +237,7 @@ class RCLValidations_test : public beast::unit_test::Suite auto prev = std::make_shared( kCreateGenesis, Rules{config.features}, - config.FEES.toFees(), + config.fees.toFees(), std::vector{}, env.app().getNodeFamily()); history.push_back(prev); diff --git a/src/test/app/Regression_test.cpp b/src/test/app/Regression_test.cpp index ee3e25e2f4..1c83e97e61 100644 --- a/src/test/app/Regression_test.cpp +++ b/src/test/app/Regression_test.cpp @@ -87,7 +87,7 @@ struct Regression_test : public beast::unit_test::Suite auto closed = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); auto expectedDrops = kInitialXrp; @@ -194,7 +194,7 @@ struct Regression_test : public beast::unit_test::Suite using namespace jtx; Env env(*this, envconfig([](std::unique_ptr cfg) { cfg->section("transaction_queue").set("minimum_txn_in_ledger_standalone", "3"); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; return cfg; })); EnvSs envs(env); diff --git a/src/test/app/SHAMapStore_test.cpp b/src/test/app/SHAMapStore_test.cpp index f03fabd92c..fc5ef02465 100644 --- a/src/test/app/SHAMapStore_test.cpp +++ b/src/test/app/SHAMapStore_test.cpp @@ -41,7 +41,7 @@ class SHAMapStore_test : public beast::unit_test::Suite static auto onlineDelete(std::unique_ptr cfg) { - cfg->LEDGER_HISTORY = kDeleteInterval; + cfg->ledgerHistory = kDeleteInterval; auto& section = cfg->section(ConfigSection::nodeDatabase()); section.set("online_delete", std::to_string(kDeleteInterval)); return cfg; diff --git a/src/test/app/Transaction_ordering_test.cpp b/src/test/app/Transaction_ordering_test.cpp index 6672b3f834..7c0721cde3 100644 --- a/src/test/app/Transaction_ordering_test.cpp +++ b/src/test/app/Transaction_ordering_test.cpp @@ -64,7 +64,7 @@ struct Transaction_ordering_test : public beast::unit_test::Suite testcase("Incorrect order"); Env env(*this, envconfig([](std::unique_ptr cfg) { - cfg->FORCE_MULTI_THREAD = false; + cfg->forceMultiThread = false; return cfg; })); @@ -102,7 +102,7 @@ struct Transaction_ordering_test : public beast::unit_test::Suite testcase("Incorrect order multiple intermediaries"); Env env(*this, envconfig([](std::unique_ptr cfg) { - cfg->FORCE_MULTI_THREAD = true; + cfg->forceMultiThread = true; return cfg; })); diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index d8882cf346..8333fce3b3 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -1259,7 +1259,7 @@ public: testcase("tie breaking"); auto cfg = makeConfig({{"minimum_txn_in_ledger_standalone", "4"}}); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env(*this, std::move(cfg)); auto alice = Account("alice"); @@ -2653,7 +2653,7 @@ public: {{"minimum_txn_in_ledger_standalone", "1"}, {"ledgers_in_queue", "10"}, {"maximum_txn_per_account", "11"}}); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env(*this, std::move(cfg)); auto const baseFee = env.current()->fees().base.drops(); @@ -4069,9 +4069,9 @@ public: {{"account_reserve", "1000"}, {"owner_reserve", "50"}}); auto& votingSection = cfg->section("voting"); - votingSection.set("account_reserve", std::to_string(cfg->FEES.reference_fee.drops() * 100)); + votingSection.set("account_reserve", std::to_string(cfg->fees.referenceFee.drops() * 100)); - votingSection.set("reference_fee", std::to_string(cfg->FEES.reference_fee.drops())); + votingSection.set("reference_fee", std::to_string(cfg->fees.referenceFee.drops())); Env env(*this, std::move(cfg)); diff --git a/src/test/app/ValidatorList_test.cpp b/src/test/app/ValidatorList_test.cpp index 1b66dd305d..20a3557db5 100644 --- a/src/test/app/ValidatorList_test.cpp +++ b/src/test/app/ValidatorList_test.cpp @@ -2257,7 +2257,7 @@ private: auto extractProtocolMessage1 = [this, &extractHeader](Message& message) { auto [header, buffers] = extractHeader(message); if (BEAST_EXPECT(header) && - BEAST_EXPECT(header->message_type == protocol::mtVALIDATOR_LIST)) + BEAST_EXPECT(header->messageType == protocol::mtVALIDATOR_LIST)) { auto const msg = detail::parseMessageContent(*header, buffers.data()); @@ -2269,7 +2269,7 @@ private: auto extractProtocolMessage2 = [this, &extractHeader](Message& message) { auto [header, buffers] = extractHeader(message); if (BEAST_EXPECT(header) && - BEAST_EXPECT(header->message_type == protocol::mtVALIDATOR_LIST_COLLECTION)) + BEAST_EXPECT(header->messageType == protocol::mtVALIDATOR_LIST_COLLECTION)) { auto const msg = detail::parseMessageContent( *header, buffers.data()); diff --git a/src/test/app/XChain_test.cpp b/src/test/app/XChain_test.cpp index 6266d5870d..0198a36e96 100644 --- a/src/test/app/XChain_test.cpp +++ b/src/test/app/XChain_test.cpp @@ -266,8 +266,8 @@ struct BalanceTransfer balance from; balance to; - balance payer; // pays the rewards - std::vector reward_accounts; // receives the reward + balance payer; // pays the rewards + std::vector rewardAccounts; // receives the reward XRPAmount txFees; BalanceTransfer( @@ -281,7 +281,7 @@ struct BalanceTransfer : from(env, fromAcct) , to(env, toAcct) , payer(env, payer) - , reward_accounts([&]() { + , rewardAccounts([&]() { std::vector r; r.reserve(numPayees); for (size_t i = 0; i < numPayees; ++i) @@ -306,7 +306,7 @@ struct BalanceTransfer [[nodiscard]] bool payeesReceived(STAmount const& reward) const { - return std::all_of(reward_accounts.begin(), reward_accounts.end(), [&](balance const& b) { + return std::all_of(rewardAccounts.begin(), rewardAccounts.end(), [&](balance const& b) { return b.diff() == reward; }); } @@ -320,7 +320,7 @@ struct BalanceTransfer bool hasHappened(STAmount const& amt, STAmount const& reward, bool checkPayer = true) { - auto rewardCost = multiply(reward, STAmount(reward_accounts.size()), reward.asset()); + auto rewardCost = multiply(reward, STAmount(rewardAccounts.size()), reward.asset()); return checkMostBalances(amt, reward) && (!checkPayer || payer.diff() == -(rewardCost + txFees)); } @@ -1007,7 +1007,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj scEnv.tx(xchainClaim(scAlice, jvb, claimID, amt, scBob)).close(); } - BEAST_EXPECT(transfer.hasHappened(amt, split_reward_quorum)); + BEAST_EXPECT(transfer.hasHappened(amt, splitRewardQuorum)); } // Check that the reward paid from a claim Id was the reward when @@ -1059,7 +1059,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // make sure the reward accounts indeed received the original // split reward (1 split 5 ways) instead of the updated 2 XRP. - BEAST_EXPECT(transfer.hasHappened(amt, split_reward_quorum)); + BEAST_EXPECT(transfer.hasHappened(amt, splitRewardQuorum)); } // Check that the signatures used to verify attestations and decide @@ -1086,7 +1086,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // change signers - claim should not be processed is the batch // is signed by original signers - scEnv.tx(jtx::signers(Account::kMaster, quorum, alt_signers)).close(); + scEnv.tx(jtx::signers(Account::kMaster, quorum, altSigners)).close(); BalanceTransfer transfer( scEnv, @@ -1120,7 +1120,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // submit claim using current signers - should succeed scEnv .multiTx(claimAttestations( - scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, alt_signers)) + scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, altSigners)) .close(); if (withClaim) { @@ -1132,7 +1132,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // make sure the transfer went through as we sent attestations // using new signers - BEAST_EXPECT(transfer.hasHappened(amt, split_reward_quorum, false)); + BEAST_EXPECT(transfer.hasHappened(amt, splitRewardQuorum, false)); } // coverage test: bridge_modify transaction with incorrect flag @@ -1220,7 +1220,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // Creating the new object would put the account below the reserve XEnv(*this, true) .tx(createBridge(Account::kMaster, jvb)) - .fund(res1 - xrp_dust, scuAlice) // barely not enough + .fund(res1 - xrpDust, scuAlice) // barely not enough .close() .tx(xchainCreateClaimId(scuAlice, jvb, reward, mcAlice), Ter(tecINSUFFICIENT_RESERVE)) .close(); @@ -1231,7 +1231,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj XEnv(*this, true) .tx(createBridge(Account::kMaster, jvb)) .close() - .tx(xchainCreateClaimId(scAlice, jvb, split_reward_quorum, mcAlice), + .tx(xchainCreateClaimId(scAlice, jvb, splitRewardQuorum, mcAlice), Ter(tecXCHAIN_REWARD_MISMATCH)) .close(); @@ -1273,7 +1273,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj testcase("Commit"); // Commit to a non-existent bridge - XEnv(*this).tx(xchainCommit(mcAlice, jvb, 1, one_xrp, scBob), Ter(tecNO_ENTRY)); + XEnv(*this).tx(xchainCommit(mcAlice, jvb, 1, oneXrp, scBob), Ter(tecNO_ENTRY)); // check that reward not deducted when doing the commit { @@ -1309,17 +1309,17 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // reserve (if XRP) XEnv(*this) .tx(createBridge(mcDoor, jvb)) - .fund(res0 + one_xrp - xrp_dust, mcuAlice) // barely not enough + .fund(res0 + oneXrp - xrpDust, mcuAlice) // barely not enough .close() - .tx(xchainCommit(mcuAlice, jvb, 1, one_xrp, scBob), Ter(tecUNFUNDED_PAYMENT)); + .tx(xchainCommit(mcuAlice, jvb, 1, oneXrp, scBob), Ter(tecUNFUNDED_PAYMENT)); XEnv(*this) .tx(createBridge(mcDoor, jvb)) .fund( - res0 + one_xrp + xrp_dust, // "xrp_dust" for tx fees - mcuAlice) // exactly enough => should succeed + res0 + oneXrp + xrpDust, // "xrp_dust" for tx fees + mcuAlice) // exactly enough => should succeed .close() - .tx(xchainCommit(mcuAlice, jvb, 1, one_xrp, scBob)); + .tx(xchainCommit(mcuAlice, jvb, 1, oneXrp, scBob)); // Commit an amount above the account's balance (for both XRP and // IOUs) @@ -1327,7 +1327,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj .tx(createBridge(mcDoor, jvb)) .fund(res0, mcuAlice) // barely not enough .close() - .tx(xchainCommit(mcuAlice, jvb, 1, res0 + one_xrp, scBob), Ter(tecUNFUNDED_PAYMENT)); + .tx(xchainCommit(mcuAlice, jvb, 1, res0 + oneXrp, scBob), Ter(tecUNFUNDED_PAYMENT)); auto jvbUsd = bridge(mcDoor, mcUSD, scGw, scUSD); @@ -1377,7 +1377,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj XEnv(*this) .tx(createBridge(mcDoor)) .close() - .tx(xchainCommit(mcAlice, jvb, 1, one_xrp, scBob), + .tx(xchainCommit(mcAlice, jvb, 1, oneXrp, scBob), Txflags(tfFillOrKill), Ter(temINVALID_FLAG)); @@ -1387,7 +1387,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj .tx(createBridge(mcDoor)) .disableFeature(featureXChainBridge) .close() - .tx(xchainCommit(mcAlice, jvb, 1, one_xrp, scBob), Ter(temDISABLED)); + .tx(xchainCommit(mcAlice, jvb, 1, oneXrp, scBob), Ter(temDISABLED)); } void @@ -1467,7 +1467,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj BEAST_EXPECT(scEnv.claimID(jvb) == claimID); } - BEAST_EXPECT(transfer.hasHappened(amt, split_reward_everyone)); + BEAST_EXPECT(transfer.hasHappened(amt, splitRewardEveryone)); } // Test that signature weights are correctly handled. Assign @@ -2208,12 +2208,12 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj std::uint32_t const claimID = 1; for (auto i = 0; i < kUtXchainDefaultNumSigners - 2; ++i) - scEnv.fund(amt, alt_signers[i].account); + scEnv.fund(amt, altSigners[i].account); mcEnv.tx(createBridge(mcDoor, jvb)).close(); scEnv.tx(createBridge(Account::kMaster, jvb)) - .tx(jtx::signers(Account::kMaster, quorum, alt_signers)) + .tx(jtx::signers(Account::kMaster, quorum, altSigners)) .close() .tx(xchainCreateClaimId(scAlice, jvb, reward, mcAlice)) .close(); @@ -2225,24 +2225,24 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj { // G1: master key auto att = claimAttestation( - scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, alt_signers[0]); + scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, altSigners[0]); scEnv.tx(att).close(); } { // G2: regular key // alt_signers[0] is the regular key of alt_signers[1] // There should be 2 attestations after the transaction - scEnv.tx(jtx::regkey(alt_signers[1].account, alt_signers[0].account)).close(); + scEnv.tx(jtx::regkey(altSigners[1].account, altSigners[0].account)).close(); auto att = claimAttestation( - scAttester, jvb, mcAlice, amt, payees[1], true, claimID, dst, alt_signers[0]); - att[sfAttestationSignerAccount.getJsonName()] = alt_signers[1].account.human(); + scAttester, jvb, mcAlice, amt, payees[1], true, claimID, dst, altSigners[0]); + att[sfAttestationSignerAccount.getJsonName()] = altSigners[1].account.human(); scEnv.tx(att).close(); } { // B3: public key and non-exist (unfunded) account mismatch // G3: public key and non-exist (unfunded) account match - auto const unfundedSigner1 = alt_signers[kUtXchainDefaultNumSigners - 1]; - auto const unfundedSigner2 = alt_signers[kUtXchainDefaultNumSigners - 2]; + auto const unfundedSigner1 = altSigners[kUtXchainDefaultNumSigners - 1]; + auto const unfundedSigner2 = altSigners[kUtXchainDefaultNumSigners - 2]; auto att = claimAttestation( scAttester, jvb, @@ -2261,7 +2261,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj { // B2: single item signer list std::vector tempSignerList = {signers[0]}; - scEnv.tx(jtx::signers(alt_signers[2].account, 1, tempSignerList)); + scEnv.tx(jtx::signers(altSigners[2].account, 1, tempSignerList)); auto att = claimAttestation( scAttester, jvb, @@ -2272,14 +2272,14 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj claimID, dst, tempSignerList.front()); - att[sfAttestationSignerAccount.getJsonName()] = alt_signers[2].account.human(); + att[sfAttestationSignerAccount.getJsonName()] = altSigners[2].account.human(); scEnv.tx(att, Ter(tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR)).close(); } { // B1: disabled master key - scEnv.tx(fset(alt_signers[2].account, asfDisableMaster, 0)).close(); + scEnv.tx(fset(altSigners[2].account, asfDisableMaster, 0)).close(); auto att = claimAttestation( - scAttester, jvb, mcAlice, amt, payees[2], true, claimID, dst, alt_signers[2]); + scAttester, jvb, mcAlice, amt, payees[2], true, claimID, dst, altSigners[2]); scEnv.tx(att, Ter(tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR)).close(); } { @@ -2292,11 +2292,11 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // --B5: missing sfAttestationSignerAccount field // Then submit the one with the field. Should reach quorum. auto att = claimAttestation( - scAttester, jvb, mcAlice, amt, payees[3], true, claimID, dst, alt_signers[3]); + scAttester, jvb, mcAlice, amt, payees[3], true, claimID, dst, altSigners[3]); att.removeMember(sfAttestationSignerAccount.getJsonName()); scEnv.tx(att, Ter(temMALFORMED)).close(); BEAST_EXPECT(dstStartBalance == scEnv.env.balance(dst)); - att[sfAttestationSignerAccount.getJsonName()] = alt_signers[3].account.human(); + att[sfAttestationSignerAccount.getJsonName()] = altSigners[3].account.human(); scEnv.tx(att).close(); BEAST_EXPECT(dstStartBalance + amt == scEnv.env.balance(dst)); } @@ -2431,7 +2431,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj scEnv.tx(xchainClaim(scAlice, jvb, claimID, amt, scBob)).close(); } - BEAST_EXPECT(transfer.hasHappened(amt, split_reward_quorum)); + BEAST_EXPECT(transfer.hasHappened(amt, splitRewardQuorum)); } // Claim with just one attestation signed by the Master key @@ -2641,7 +2641,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj } else { - BEAST_EXPECT(transfer.hasHappened(amt, split_reward_quorum)); + BEAST_EXPECT(transfer.hasHappened(amt, splitRewardQuorum)); } } @@ -3180,7 +3180,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // claim wrong amount scEnv - .tx(xchainClaim(scAlice, jvb, claimID, one_xrp, scBob), + .tx(xchainClaim(scAlice, jvb, claimID, oneXrp, scBob), Ter(tecXCHAIN_CLAIM_NO_QUORUM)) .close(); } @@ -3232,7 +3232,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj claimCost += fee; } - BEAST_EXPECT(transfer.hasHappened(amt, split_reward_quorum)); + BEAST_EXPECT(transfer.hasHappened(amt, splitRewardQuorum)); BEAST_EXPECT(scAliceBal.diff() == -claimCost); // because reward % 4 == 0 } @@ -3244,12 +3244,12 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj XEnv mcEnv(*this); XEnv scEnv(*this, true); - mcEnv.tx(createBridge(mcDoor, jvb, tiny_reward)).close(); + mcEnv.tx(createBridge(mcDoor, jvb, tinyReward)).close(); - scEnv.tx(createBridge(Account::kMaster, jvb, tiny_reward)) + scEnv.tx(createBridge(Account::kMaster, jvb, tinyReward)) .tx(jtx::signers(Account::kMaster, quorum, signers)) .close() - .tx(xchainCreateClaimId(scAlice, jvb, tiny_reward, mcAlice)) + .tx(xchainCreateClaimId(scAlice, jvb, tinyReward, mcAlice)) .close(); auto dst(withClaim ? std::nullopt : std::optional{scBob}); @@ -3268,7 +3268,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj test::Balance const scAliceBal(scEnv, scAlice); scEnv.multiTx(claimAttestations( scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers)); - STAmount claimCost = tiny_reward; + STAmount claimCost = tinyReward; if (withClaim) { @@ -3279,8 +3279,8 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj claimCost += fee; } - BEAST_EXPECT(transfer.hasHappened(amt, tiny_reward_split)); - BEAST_EXPECT(scAliceBal.diff() == -(claimCost - tiny_reward_remainder)); + BEAST_EXPECT(transfer.hasHappened(amt, tinyRewardSplit)); + BEAST_EXPECT(scAliceBal.diff() == -(claimCost - tinyRewardRemainder)); } // If a reward distribution fails for one of the reward accounts @@ -3331,7 +3331,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // this also checks that only 3 * split_reward was deducted from // scAlice (the payer account), since we passed alt_payees to // BalanceTransfer - BEAST_EXPECT(transfer.hasHappened(amt, split_reward_quorum)); + BEAST_EXPECT(transfer.hasHappened(amt, splitRewardQuorum)); } for (auto withClaim : {false, true}) @@ -3381,7 +3381,7 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // this also checks that only 3 * split_reward was deducted from // scAlice (the payer account), since we passed payees.size() - // 1 to BalanceTransfer - BEAST_EXPECT(transfer.hasHappened(amt, split_reward_quorum)); + BEAST_EXPECT(transfer.hasHappened(amt, splitRewardQuorum)); // and make sure the account with deposit auth received nothing BEAST_EXPECT(lastSigner.diff() == STAmount(0)); @@ -3578,17 +3578,17 @@ struct XChain_test : public beast::unit_test::Suite, public jtx::XChainBridgeObj // commit where the fee dips into the reserve, this should succeed XEnv(*this) .tx(createBridge(mcDoor, jvb)) - .fund(res0 + one_xrp + fee - drops(1), mcuAlice) + .fund(res0 + oneXrp + fee - drops(1), mcuAlice) .close() - .tx(xchainCommit(mcuAlice, jvb, 1, one_xrp, scBob), Ter(tesSUCCESS)); + .tx(xchainCommit(mcuAlice, jvb, 1, oneXrp, scBob), Ter(tesSUCCESS)); // commit where the commit amount drips into the reserve, this should // fail XEnv(*this) .tx(createBridge(mcDoor, jvb)) - .fund(res0 + one_xrp - drops(1), mcuAlice) + .fund(res0 + oneXrp - drops(1), mcuAlice) .close() - .tx(xchainCommit(mcuAlice, jvb, 1, one_xrp, scBob), Ter(tecUNFUNDED_PAYMENT)); + .tx(xchainCommit(mcuAlice, jvb, 1, oneXrp, scBob), Ter(tecUNFUNDED_PAYMENT)); auto const minAccountCreate = XRP(20); @@ -3757,8 +3757,8 @@ private: jtx::Account finaldest; STAmount amt; bool a2b; // direction of transfer - WithClaim with_claim{WithClaim::No}; - uint32_t claim_id{0}; + WithClaim withClaim{WithClaim::No}; + uint32_t claimId{0}; std::array attested{}; }; @@ -3769,7 +3769,7 @@ private: STAmount amt; STAmount reward; bool a2b; - uint32_t claim_id{0}; + uint32_t claimId{0}; std::array attested{}; }; @@ -3806,7 +3806,7 @@ private: using CreateClaimVec = jtx::JValueVec; using CreateClaimMap = std::map; - ChainStateTrack(ENV& env) : env(env), tx_fee(env.env.current()->fees().base) + ChainStateTrack(ENV& env) : env(env), txFee(env.env.current()->fees().base) { } @@ -3851,31 +3851,30 @@ private: { callbackCalled = false; // cspell: ignore attns - for (size_t i = 0; i < signers_attns.size(); ++i) + for (size_t i = 0; i < signersAttns.size(); ++i) { - for (auto& [bridge, claims] : signers_attns[i]) + for (auto& [bridge, claims] : signersAttns[i]) { - sendAttestations(i, bridge, claims.xfer_claims); + sendAttestations(i, bridge, claims.xferClaims); auto& c = counters[bridge]; - auto& createClaims = claims.create_claims[c.claim_count]; + auto& createClaims = claims.createClaims[c.claimCount]; auto numAttns = createClaims.size(); if (numAttns != 0u) { - c.num_create_attn_sent += - sendCreateAttestations(i, bridge, createClaims); + c.numCreateAttnSent += sendCreateAttestations(i, bridge, createClaims); } - assert(claims.create_claims[c.claim_count].empty()); + assert(claims.createClaims[c.claimCount].empty()); } } for (auto& [bridge, c] : counters) { - if (c.num_create_attn_sent >= bridge->quorum) + if (c.numCreateAttnSent >= bridge->quorum) { callbackCalled = true; - c.create_callbacks[c.claim_count](c.signers); - ++c.claim_count; - c.num_create_attn_sent = 0; + c.createCallbacks[c.claimCount](c.signers); + ++c.claimCount; + c.numCreateAttnSent = 0; c.signers.clear(); } } @@ -3926,7 +3925,7 @@ private: void spendFee(jtx::Account const& acct, size_t times = 1) { - spend(acct, tx_fee, times); + spend(acct, txFee, times); } [[nodiscard]] bool @@ -3944,20 +3943,20 @@ private: { using complete_cb = std::function const& signers)>; - uint32_t claim_id{0}; - uint32_t create_count{0}; // for account create. First should be 1 - uint32_t claim_count{0}; // for account create. Increments after quorum for - // current create_count (starts at 1) is reached. + uint32_t claimId{0}; + uint32_t createCount{0}; // for account create. First should be 1 + uint32_t claimCount{0}; // for account create. Increments after quorum for + // current createCount (starts at 1) is reached. - uint32_t num_create_attn_sent{0}; // for current claim_count + uint32_t numCreateAttnSent{0}; // for current claimCount std::vector signers; - std::vector create_callbacks; + std::vector createCallbacks; }; struct Claims { - ClaimVec xfer_claims; - CreateClaimMap create_claims; + ClaimVec xferClaims; + CreateClaimMap createClaims; }; using SignerAttns = std::unordered_map; @@ -3965,9 +3964,9 @@ private: ENV& env; std::map accounts; - SignersAttns signers_attns; + SignersAttns signersAttns; std::map counters; - STAmount tx_fee; + STAmount txFee; }; struct ChainStateTracker @@ -4086,7 +4085,7 @@ private: st.transfer(cr_.from, srcdoor, cr_.amt); st.transfer(cr_.from, srcdoor, cr_.reward); - return ++st.counters[&bridge_].create_count; + return ++st.counters[&bridge_].createCount; } void @@ -4105,9 +4104,8 @@ private: // enqueue one attestation for this signer cr_.attested[signerIdx] = true; - st.signers_attns[signerIdx][&bridge_] - .create_claims[cr_.claim_id - 1] - .emplace_back(createAccountAttestation( + st.signersAttns[signerIdx][&bridge_].createClaims[cr_.claimId - 1].emplace_back( + createAccountAttestation( bridge_.signers[signerIdx].account, bridge_.jvb, cr_.from, @@ -4115,7 +4113,7 @@ private: cr_.reward, bridge_.signers[signerIdx].account, cr_.a2b, - cr_.claim_id, + cr_.claimId, cr_.to, bridge_.signers[signerIdx])); break; @@ -4126,15 +4124,15 @@ private: return; // did not attest auto& counters = st.counters[&bridge_]; - if (counters.create_callbacks.size() < cr_.claim_id) - counters.create_callbacks.resize(cr_.claim_id); + if (counters.createCallbacks.size() < cr_.claimId) + counters.createCallbacks.resize(cr_.claimId); auto completeCb = [&](std::vector const& signers) { auto numAttestors = signers.size(); st.env.close(); assert(numAttestors <= std::count(cr_.attested.begin(), cr_.attested.end(), true)); assert(numAttestors >= bridge_.quorum); - assert(cr_.claim_id - 1 == counters.claim_count); + assert(cr_.claimId - 1 == counters.claimCount); auto r = cr_.reward; auto reward = divide(r, STAmount(numAttestors), r.asset()); @@ -4145,20 +4143,20 @@ private: st.spend(dstDoor(), reward, numAttestors); st.transfer(dstDoor(), cr_.to, cr_.amt); st.env.env.memoize(cr_.to); - sm_state_ = SmState::Completed; + smState_ = SmState::Completed; }; - counters.create_callbacks[cr_.claim_id - 1] = std::move(completeCb); + counters.createCallbacks[cr_.claimId - 1] = std::move(completeCb); } SmState advance(uint64_t time, uint32_t rnd) { - switch (sm_state_) + switch (smState_) { case SmState::Initial: - cr_.claim_id = issueAccountCreate(); - sm_state_ = SmState::Attesting; + cr_.claimId = issueAccountCreate(); + smState_ = SmState::Attesting; break; case SmState::Attesting: @@ -4172,11 +4170,11 @@ private: case SmState::Completed: break; // will get this once } - return sm_state_; + return smState_; } private: - SmState sm_state_{SmState::Initial}; + SmState smState_{SmState::Initial}; AccountCreate cr_; }; @@ -4209,7 +4207,7 @@ private: .close(); // needed for claim_id sequence to be // correct' st.spendFee(xfer_.to); - return ++st.counters[&bridge_].claim_id; + return ++st.counters[&bridge_].claimId; } void @@ -4226,10 +4224,10 @@ private: st.env.tx(xchainCommit( xfer_.from, bridge_.jvb, - xfer_.claim_id, + xfer_.claimId, xfer_.amt, - xfer_.with_claim == WithClaim::Yes ? std::nullopt - : std::optional(xfer_.finaldest))); + xfer_.withClaim == WithClaim::Yes ? std::nullopt + : std::optional(xfer_.finaldest))); st.spendFee(xfer_.from); st.transfer(xfer_.from, srcdoor, xfer_.amt); } @@ -4262,15 +4260,15 @@ private: // enqueue one attestation for this signer xfer_.attested[signerIdx] = true; - st.signers_attns[signerIdx][&bridge_].xfer_claims.emplace_back(claimAttestation( + st.signersAttns[signerIdx][&bridge_].xferClaims.emplace_back(claimAttestation( bridge_.signers[signerIdx].account, bridge_.jvb, xfer_.from, xfer_.amt, bridge_.signers[signerIdx].account, xfer_.a2b, - xfer_.claim_id, - xfer_.with_claim == WithClaim::Yes + xfer_.claimId, + xfer_.withClaim == WithClaim::Yes ? std::nullopt : std::optional(xfer_.finaldest), bridge_.signers[signerIdx])); @@ -4281,7 +4279,7 @@ private: // return true if quorum was reached, false otherwise bool const quorum = std::count(xfer_.attested.begin(), xfer_.attested.end(), true) >= bridge_.quorum; - if (quorum && xfer_.with_claim == WithClaim::No) + if (quorum && xfer_.withClaim == WithClaim::No) { distributeReward(st); st.transfer(dstDoor(), xfer_.finaldest, xfer_.amt); @@ -4294,7 +4292,7 @@ private: { ChainStateTrack& st = destState(); st.env.tx( - xchainClaim(xfer_.to, bridge_.jvb, xfer_.claim_id, xfer_.amt, xfer_.finaldest)); + xchainClaim(xfer_.to, bridge_.jvb, xfer_.claimId, xfer_.amt, xfer_.finaldest)); distributeReward(st); st.transfer(dstDoor(), xfer_.finaldest, xfer_.amt); st.spendFee(xfer_.to); @@ -4303,34 +4301,34 @@ private: SmState advance(uint64_t time, uint32_t rnd) { - switch (sm_state_) + switch (smState_) { case SmState::Initial: - xfer_.claim_id = createClaimId(); - sm_state_ = SmState::ClaimIdCreated; + xfer_.claimId = createClaimId(); + smState_ = SmState::ClaimIdCreated; break; case SmState::ClaimIdCreated: commit(); - sm_state_ = SmState::Attesting; + smState_ = SmState::Attesting; break; case SmState::Attesting: if (attest(time, rnd)) { - sm_state_ = xfer_.with_claim == WithClaim::Yes ? SmState::Attested - : SmState::Completed; + smState_ = xfer_.withClaim == WithClaim::Yes ? SmState::Attested + : SmState::Completed; } else { - sm_state_ = SmState::Attesting; + smState_ = SmState::Attesting; } break; case SmState::Attested: - assert(xfer_.with_claim == WithClaim::Yes); + assert(xfer_.withClaim == WithClaim::Yes); claim(); - sm_state_ = SmState::Completed; + smState_ = SmState::Completed; break; default: @@ -4338,12 +4336,12 @@ private: assert(0); // should have been removed break; } - return sm_state_; + return smState_; } private: Transfer xfer_; - SmState sm_state_{SmState::Initial}; + SmState smState_{SmState::Initial}; }; // -------------------------------------------------- @@ -4573,7 +4571,7 @@ public: .finaldest = a[1], .amt = XRP(6), .a2b = true, - .with_claim = WithClaim::No}); + .withClaim = WithClaim::No}); xfer( 1, st, @@ -4583,7 +4581,7 @@ public: .finaldest = a[1], .amt = XRP(8), .a2b = false, - .with_claim = WithClaim::No}); + .withClaim = WithClaim::No}); xfer( 1, st, xrpB, {.from = a[1], .to = a[1], .finaldest = a[1], .amt = XRP(1), .a2b = true}); xfer( @@ -4605,7 +4603,7 @@ public: .finaldest = a[1], .amt = XRP(7), .a2b = false, - .with_claim = WithClaim::No}); + .withClaim = WithClaim::No}); xfer( 2, st, xrpB, {.from = a[1], .to = a[1], .finaldest = a[1], .amt = XRP(9), .a2b = true}); runSimulation(st); diff --git a/src/test/consensus/Consensus_test.cpp b/src/test/consensus/Consensus_test.cpp index b8fed1d981..6cc8d4fde1 100644 --- a/src/test/consensus/Consensus_test.cpp +++ b/src/test/consensus/Consensus_test.cpp @@ -453,7 +453,7 @@ public: // Vary the time it takes to process validations to exercise detecting // the wrong LCL at different phases of consensus - for (auto validationDelay : {0ms, parms.ledgerMIN_CLOSE}) + for (auto validationDelay : {0ms, parms.ledgerMinClose}) { // Consider 10 peers: // 0 1 2 3 4 5 6 7 8 9 @@ -492,7 +492,7 @@ public: CollectByNode jumps; sim.collectors.add(jumps); - BEAST_EXPECT(sim.trustGraph.canFork(parms.minCONSENSUS_PCT / 100.)); + BEAST_EXPECT(sim.trustGraph.canFork(parms.minConsensusPct / 100.)); // initial round to set prior state sim.run(1); diff --git a/src/test/consensus/NegativeUNL_test.cpp b/src/test/consensus/NegativeUNL_test.cpp index 05dc3f0e2f..142077a42f 100644 --- a/src/test/consensus/NegativeUNL_test.cpp +++ b/src/test/consensus/NegativeUNL_test.cpp @@ -239,7 +239,7 @@ class NegativeUNL_test : public beast::unit_test::Suite auto l = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); @@ -554,12 +554,12 @@ struct NetworkHistory createNodes() { assert(param.numNodes <= 256); - UNLKeys = createPublicKeys(param.numNodes); + unlKeys = createPublicKeys(param.numNodes); for (int i = 0; i < param.numNodes; ++i) { - UNLKeySet.insert(UNLKeys[i]); - UNLNodeIDs.push_back(calcNodeID(UNLKeys[i])); - UNLNodeIDSet.insert(UNLNodeIDs.back()); + unlKeySet.insert(unlKeys[i]); + unlNodeIDs.push_back(calcNodeID(unlKeys[i])); + unlNodeIdSet.insert(unlNodeIDs.back()); } } @@ -574,7 +574,7 @@ struct NetworkHistory auto l = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{kFakeAmendment++}, env.app().getNodeFamily()); history.push_back(l); @@ -593,7 +593,7 @@ struct NetworkHistory OpenView accum(&*l); if (l->negativeUNL().size() < param.negUNLSize) { - auto tx = createTx(true, l->seq(), UNLKeys[nidx]); + auto tx = createTx(true, l->seq(), unlKeys[nidx]); if (!applyAndTestResult(env, accum, tx, true)) break; ++nidx; @@ -602,14 +602,14 @@ struct NetworkHistory { if (param.hasToDisable) { - auto tx = createTx(true, l->seq(), UNLKeys[nidx]); + auto tx = createTx(true, l->seq(), unlKeys[nidx]); if (!applyAndTestResult(env, accum, tx, true)) break; ++nidx; } if (param.hasToReEnable) { - auto tx = createTx(false, l->seq(), UNLKeys[0]); + auto tx = createTx(false, l->seq(), unlKeys[0]); if (!applyAndTestResult(env, accum, tx, true)) break; } @@ -665,9 +665,9 @@ struct NetworkHistory { if (needVal(history[curr], i)) { - RCLValidation v(createSTVal(history[curr], UNLNodeIDs[i])); + RCLValidation v(createSTVal(history[curr], unlNodeIDs[i])); v.setTrusted(); - validations.add(UNLNodeIDs[i], v); + validations.add(unlNodeIDs[i], v); } } } @@ -682,10 +682,10 @@ struct NetworkHistory jtx::Env env; Parameter param; RCLValidations& validations; - std::vector UNLKeys; - hash_set UNLKeySet; - std::vector UNLNodeIDs; - hash_set UNLNodeIDSet; + std::vector unlKeys; + hash_set unlKeySet; + std::vector unlNodeIDs; + hash_set unlNodeIdSet; LedgerHistory history; bool goodHistory; }; @@ -714,7 +714,7 @@ voteAndCheck( pre(vote); auto txSet = std::make_shared(SHAMapType::TRANSACTION, history.env.app().getNodeFamily()); - vote.doVoting(history.lastLedger(), history.UNLKeySet, history.validations, txSet); + vote.doVoting(history.lastLedger(), history.unlKeySet, history.validations, txSet); return countTx(txSet) == expect; } @@ -795,9 +795,9 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite BEAST_EXPECT(history.goodHistory); if (history.goodHistory) { - NegativeUNLVote vote(history.UNLNodeIDs[3], history.env.journal); + NegativeUNLVote vote(history.unlNodeIDs[3], history.env.journal); BEAST_EXPECT(!vote.buildScoreTable( - history.lastLedger(), history.UNLNodeIDSet, history.validations)); + history.lastLedger(), history.unlNodeIdSet, history.validations)); } } @@ -813,9 +813,9 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite BEAST_EXPECT(history.goodHistory); if (history.goodHistory) { - NegativeUNLVote vote(history.UNLNodeIDs[3], history.env.journal); + NegativeUNLVote vote(history.unlNodeIDs[3], history.env.journal); BEAST_EXPECT(!vote.buildScoreTable( - history.lastLedger(), history.UNLNodeIDSet, history.validations)); + history.lastLedger(), history.unlNodeIdSet, history.validations)); } } @@ -831,15 +831,15 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite BEAST_EXPECT(history.goodHistory); if (history.goodHistory) { - NodeID myId = history.UNLNodeIDs[3]; + NodeID myId = history.unlNodeIDs[3]; history.walkHistoryAndAddValidations( [&](std::shared_ptr const& l, std::size_t idx) -> bool { // skip half my validations. - return history.UNLNodeIDs[idx] != myId || l->seq() % 2 != 0; + return history.unlNodeIDs[idx] != myId || l->seq() % 2 != 0; }); NegativeUNLVote vote(myId, history.env.journal); BEAST_EXPECT(!vote.buildScoreTable( - history.lastLedger(), history.UNLNodeIDSet, history.validations)); + history.lastLedger(), history.unlNodeIdSet, history.validations)); } } @@ -864,12 +864,12 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite if (history.goodHistory && wrongChainSuccess) { - NodeID myId = history.UNLNodeIDs[3]; - NodeID const badNode = history.UNLNodeIDs[4]; + NodeID myId = history.unlNodeIDs[3]; + NodeID const badNode = history.unlNodeIDs[4]; history.walkHistoryAndAddValidations( [&](std::shared_ptr const& l, std::size_t idx) -> bool { // everyone but me - return !(history.UNLNodeIDs[idx] == myId); + return !(history.unlNodeIDs[idx] == myId); }); // local node validate wrong chain @@ -887,7 +887,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite // local node still on wrong chain, can build a scoreTable, // but all other nodes' scores are zero auto scoreTable = vote.buildScoreTable( - wrongChain.back(), history.UNLNodeIDSet, history.validations); + wrongChain.back(), history.unlNodeIdSet, history.validations); BEAST_EXPECT(scoreTable); if (scoreTable) { @@ -907,7 +907,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite // if local node switched to right history, but cannot build // scoreTable because not enough local validations BEAST_EXPECT(!vote.buildScoreTable( - history.lastLedger(), history.UNLNodeIDSet, history.validations)); + history.lastLedger(), history.unlNodeIdSet, history.validations)); } } @@ -927,9 +927,9 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite [&](std::shared_ptr const& l, std::size_t idx) -> bool { return true; }); - NegativeUNLVote vote(history.UNLNodeIDs[3], history.env.journal); + NegativeUNLVote vote(history.unlNodeIDs[3], history.env.journal); auto scoreTable = vote.buildScoreTable( - history.lastLedger(), history.UNLNodeIDSet, history.validations); + history.lastLedger(), history.unlNodeIdSet, history.validations); BEAST_EXPECT(scoreTable); if (scoreTable) { @@ -1001,35 +1001,35 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite hash_set negUnl012; for (std::uint32_t i = 0; i < 3; ++i) - negUnl012.insert(history.UNLNodeIDs[i]); + negUnl012.insert(history.unlNodeIDs[i]); // build a good scoreTable to use, or copy and modify hash_map goodScoreTable; - for (auto const& n : history.UNLNodeIDs) + for (auto const& n : history.unlNodeIDs) goodScoreTable[n] = NegativeUNLVote::kNegativeUnlHighWaterMark + 1; - NegativeUNLVote vote(history.UNLNodeIDs[0], history.env.journal); + NegativeUNLVote vote(history.unlNodeIDs[0], history.env.journal); { // all good scores BEAST_EXPECT( - checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl012, goodScoreTable, 0, 3)); + checkCandidateSizes(vote, history.unlNodeIdSet, negUnl012, goodScoreTable, 0, 3)); } { // all bad scores hash_map scoreTable; - for (auto& n : history.UNLNodeIDs) + for (auto& n : history.unlNodeIDs) scoreTable[n] = NegativeUNLVote::kNegativeUnlLowWaterMark - 1; BEAST_EXPECT( - checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl012, scoreTable, 35 - 3, 0)); + checkCandidateSizes(vote, history.unlNodeIdSet, negUnl012, scoreTable, 35 - 3, 0)); } { // all between watermarks hash_map scoreTable; - for (auto& n : history.UNLNodeIDs) + for (auto& n : history.unlNodeIDs) scoreTable[n] = NegativeUNLVote::kNegativeUnlLowWaterMark + 1; BEAST_EXPECT( - checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl012, scoreTable, 0, 0)); + checkCandidateSizes(vote, history.unlNodeIdSet, negUnl012, scoreTable, 0, 0)); } { @@ -1037,36 +1037,36 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite auto scoreTable = goodScoreTable; scoreTable[*negUnl012.begin()] = NegativeUNLVote::kNegativeUnlLowWaterMark + 1; BEAST_EXPECT( - checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl012, scoreTable, 0, 2)); + checkCandidateSizes(vote, history.unlNodeIdSet, negUnl012, scoreTable, 0, 2)); } { // 2 bad scorers not in negUnl auto scoreTable = goodScoreTable; - scoreTable[history.UNLNodeIDs[11]] = NegativeUNLVote::kNegativeUnlLowWaterMark - 1; - scoreTable[history.UNLNodeIDs[12]] = NegativeUNLVote::kNegativeUnlLowWaterMark - 1; + scoreTable[history.unlNodeIDs[11]] = NegativeUNLVote::kNegativeUnlLowWaterMark - 1; + scoreTable[history.unlNodeIDs[12]] = NegativeUNLVote::kNegativeUnlLowWaterMark - 1; BEAST_EXPECT( - checkCandidateSizes(vote, history.UNLNodeIDSet, negUnl012, scoreTable, 2, 3)); + checkCandidateSizes(vote, history.unlNodeIdSet, negUnl012, scoreTable, 2, 3)); } { // 2 in negUnl but not in unl, have a remove candidate from score // table - hash_set unlTemp = history.UNLNodeIDSet; - unlTemp.erase(history.UNLNodeIDs[0]); - unlTemp.erase(history.UNLNodeIDs[1]); + hash_set unlTemp = history.unlNodeIdSet; + unlTemp.erase(history.unlNodeIDs[0]); + unlTemp.erase(history.unlNodeIDs[1]); BEAST_EXPECT(checkCandidateSizes(vote, unlTemp, negUnl012, goodScoreTable, 0, 3)); } { // 2 in negUnl but not in unl, no remove candidate from score table auto scoreTable = goodScoreTable; - scoreTable.erase(history.UNLNodeIDs[0]); - scoreTable.erase(history.UNLNodeIDs[1]); - scoreTable[history.UNLNodeIDs[2]] = NegativeUNLVote::kNegativeUnlLowWaterMark + 1; - hash_set unlTemp = history.UNLNodeIDSet; - unlTemp.erase(history.UNLNodeIDs[0]); - unlTemp.erase(history.UNLNodeIDs[1]); + scoreTable.erase(history.unlNodeIDs[0]); + scoreTable.erase(history.unlNodeIDs[1]); + scoreTable[history.unlNodeIDs[2]] = NegativeUNLVote::kNegativeUnlLowWaterMark + 1; + hash_set unlTemp = history.unlNodeIdSet; + unlTemp.erase(history.unlNodeIDs[0]); + unlTemp.erase(history.unlNodeIDs[1]); BEAST_EXPECT(checkCandidateSizes(vote, unlTemp, negUnl012, scoreTable, 0, 2)); } @@ -1075,7 +1075,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite NodeID const new1(0xbead); NodeID const new2(0xbeef); hash_set const nowTrusted = {new1, new2}; - hash_set unlTemp = history.UNLNodeIDSet; + hash_set unlTemp = history.unlNodeIdSet; unlTemp.insert(new1); unlTemp.insert(new2); vote.newValidators(256, nowTrusted); @@ -1381,7 +1381,7 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::Suite BEAST_EXPECT(history.goodHistory); if (history.goodHistory) { - NodeID myId = history.UNLNodeIDs[3]; + NodeID myId = history.unlNodeIDs[3]; history.walkHistoryAndAddValidations( [&](std::shared_ptr const& l, std::size_t idx) -> bool { std::size_t k = 0; @@ -1400,19 +1400,19 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::Suite bool const add50 = scorePattern[sp][k] == 50 && l->seq() % 2 == 0; bool const add100 = scorePattern[sp][k] == 100; - bool const addMe = history.UNLNodeIDs[idx] == myId; + bool const addMe = history.unlNodeIDs[idx] == myId; return add50 || add100 || addMe; }); NegativeUNLVote vote(myId, history.env.journal); auto scoreTable = vote.buildScoreTable( - history.lastLedger(), history.UNLNodeIDSet, history.validations); + history.lastLedger(), history.unlNodeIdSet, history.validations); BEAST_EXPECT(scoreTable); if (scoreTable) { std::uint32_t i = 0; // looping unl auto checkScores = [&](std::uint32_t score, std::uint32_t k) -> bool { - if (history.UNLNodeIDs[i] == myId) + if (history.unlNodeIDs[i] == myId) return score == 256; if (scorePattern[sp][k] == 0) return score == 0; @@ -1427,15 +1427,15 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::Suite }; for (; i < 2; ++i) { - BEAST_EXPECT(checkScores((*scoreTable)[history.UNLNodeIDs[i]], 0)); + BEAST_EXPECT(checkScores((*scoreTable)[history.unlNodeIDs[i]], 0)); } for (; i < 4; ++i) { - BEAST_EXPECT(checkScores((*scoreTable)[history.UNLNodeIDs[i]], 1)); + BEAST_EXPECT(checkScores((*scoreTable)[history.unlNodeIDs[i]], 1)); } for (; i < unlSize; ++i) { - BEAST_EXPECT(checkScores((*scoreTable)[history.UNLNodeIDs[i]], 2)); + BEAST_EXPECT(checkScores((*scoreTable)[history.unlNodeIDs[i]], 2)); } } } @@ -1506,7 +1506,7 @@ class NegativeUNLVoteGoodScore_test : public beast::unit_test::Suite [&](std::shared_ptr const& l, std::size_t idx) -> bool { return true; }); - BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 0)); + BEAST_EXPECT(voteAndCheck(history, history.unlNodeIDs[0], 0)); } } @@ -1527,7 +1527,7 @@ class NegativeUNLVoteGoodScore_test : public beast::unit_test::Suite [&](std::shared_ptr const& l, std::size_t idx) -> bool { return true; }); - BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 1)); + BEAST_EXPECT(voteAndCheck(history, history.unlNodeIDs[0], 1)); } } } @@ -1564,7 +1564,7 @@ class NegativeUNLVoteOffline_test : public beast::unit_test::Suite // skip node 0 and node 1 return idx > 1; }); - BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs.back(), 1)); + BEAST_EXPECT(voteAndCheck(history, history.unlNodeIDs.back(), 1)); } } @@ -1587,9 +1587,9 @@ class NegativeUNLVoteOffline_test : public beast::unit_test::Suite history.walkHistoryAndAddValidations( [&](std::shared_ptr const& l, std::size_t idx) -> bool { // skip node 0 and node 1 - return history.UNLNodeIDs[idx] != n1 && history.UNLNodeIDs[idx] != n2; + return history.unlNodeIDs[idx] != n1 && history.unlNodeIDs[idx] != n2; }); - BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs.back(), 0)); + BEAST_EXPECT(voteAndCheck(history, history.unlNodeIDs.back(), 0)); } } } @@ -1626,7 +1626,7 @@ class NegativeUNLVoteMaxListed_test : public beast::unit_test::Suite // skip node 0 ~ 10 return idx > 10; }); - BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs.back(), 0)); + BEAST_EXPECT(voteAndCheck(history, history.unlNodeIDs.back(), 0)); } } } @@ -1662,7 +1662,7 @@ class NegativeUNLVoteRetiredValidator_test : public beast::unit_test::Suite [&](std::shared_ptr const& l, std::size_t idx) -> bool { return idx > 1; }); - BEAST_EXPECT(voteAndCheck(history, history.UNLNodeIDs[0], 0)); + BEAST_EXPECT(voteAndCheck(history, history.unlNodeIDs[0], 0)); } } @@ -1705,9 +1705,9 @@ class NegativeUNLVoteRetiredValidator_test : public beast::unit_test::Suite return idx > 1; }); BEAST_EXPECT( - voteAndCheck(history, history.UNLNodeIDs.back(), 1, [&](NegativeUNLVote& vote) { - history.UNLKeySet.erase(history.UNLKeys[0]); - history.UNLKeySet.erase(history.UNLKeys[1]); + voteAndCheck(history, history.unlNodeIDs.back(), 1, [&](NegativeUNLVote& vote) { + history.unlKeySet.erase(history.unlKeys[0]); + history.unlKeySet.erase(history.unlKeys[1]); })); } } @@ -1745,11 +1745,11 @@ class NegativeUNLVoteNewValidator_test : public beast::unit_test::Suite return true; }); BEAST_EXPECT( - voteAndCheck(history, history.UNLNodeIDs[0], 0, [&](NegativeUNLVote& vote) { + voteAndCheck(history, history.unlNodeIDs[0], 0, [&](NegativeUNLVote& vote) { auto extraKey1 = randomKeyPair(KeyType::Ed25519).first; auto extraKey2 = randomKeyPair(KeyType::Ed25519).first; - history.UNLKeySet.insert(extraKey1); - history.UNLKeySet.insert(extraKey2); + history.unlKeySet.insert(extraKey1); + history.unlKeySet.insert(extraKey2); hash_set nowTrusted; nowTrusted.insert(calcNodeID(extraKey1)); nowTrusted.insert(calcNodeID(extraKey2)); @@ -1776,11 +1776,11 @@ class NegativeUNLVoteNewValidator_test : public beast::unit_test::Suite return true; }); BEAST_EXPECT( - voteAndCheck(history, history.UNLNodeIDs[0], 1, [&](NegativeUNLVote& vote) { + voteAndCheck(history, history.unlNodeIDs[0], 1, [&](NegativeUNLVote& vote) { auto extraKey1 = randomKeyPair(KeyType::Ed25519).first; auto extraKey2 = randomKeyPair(KeyType::Ed25519).first; - history.UNLKeySet.insert(extraKey1); - history.UNLKeySet.insert(extraKey2); + history.unlKeySet.insert(extraKey1); + history.unlKeySet.insert(extraKey2); hash_set nowTrusted; nowTrusted.insert(calcNodeID(extraKey1)); nowTrusted.insert(calcNodeID(extraKey2)); @@ -1807,7 +1807,7 @@ class NegativeUNLVoteFilterValidations_test : public beast::unit_test::Suite auto l = std::make_shared( kCreateGenesis, Rules{env.app().config().features}, - env.app().config().FEES.toFees(), + env.app().config().fees.toFees(), std::vector{}, env.app().getNodeFamily()); diff --git a/src/test/consensus/Validations_test.cpp b/src/test/consensus/Validations_test.cpp index fb453b0791..606c6f2824 100644 --- a/src/test/consensus/Validations_test.cpp +++ b/src/test/consensus/Validations_test.cpp @@ -324,15 +324,15 @@ class Validations_test : public beast::unit_test::Suite BEAST_EXPECT( ValStatus::Stale == - harness.add(n.validate(ledgerA, -harness.parms().validationCURRENT_EARLY, 0s))); + harness.add(n.validate(ledgerA, -harness.parms().validationCurrentEarly, 0s))); BEAST_EXPECT( ValStatus::Stale == - harness.add(n.validate(ledgerA, harness.parms().validationCURRENT_WALL, 0s))); + harness.add(n.validate(ledgerA, harness.parms().validationCurrentWall, 0s))); BEAST_EXPECT( ValStatus::Stale == - harness.add(n.validate(ledgerA, 0s, harness.parms().validationCURRENT_LOCAL))); + harness.add(n.validate(ledgerA, 0s, harness.parms().validationCurrentLocal))); } { @@ -357,7 +357,7 @@ class Validations_test : public beast::unit_test::Suite // If we advance far enough for AB to expire, we can fully // validate or partially validate that sequence number again BEAST_EXPECT(ValStatus::Conflicting == process(ledgerAZ)); - harness.clock().advance(harness.parms().validationSET_EXPIRES + 1ms); + harness.clock().advance(harness.parms().validationSetExpires + 1ms); BEAST_EXPECT(ValStatus::Current == process(ledgerAZ)); } } @@ -392,7 +392,7 @@ class Validations_test : public beast::unit_test::Suite BEAST_EXPECT( harness.vals().getPreferred(genesisLedger_) == std::make_pair(ledgerAB.seq(), ledgerAB.id())); - harness.clock().advance(harness.parms().validationCURRENT_LOCAL); + harness.clock().advance(harness.parms().validationCurrentLocal); // trigger check for stale trigger(harness.vals()); @@ -487,7 +487,7 @@ class Validations_test : public beast::unit_test::Suite BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == ledgerAC.seq()); // Pass enough time for it to go stale - harness.clock().advance(harness.parms().validationCURRENT_LOCAL); + harness.clock().advance(harness.parms().validationCurrentLocal); BEAST_EXPECT(harness.vals().currentTrusted().empty()); } @@ -528,7 +528,7 @@ class Validations_test : public beast::unit_test::Suite } // Pass enough time for them to go stale - harness.clock().advance(harness.parms().validationCURRENT_LOCAL); + harness.clock().advance(harness.parms().validationCurrentLocal); BEAST_EXPECT(harness.vals().getCurrentNodeIDs().empty()); } @@ -657,7 +657,7 @@ class Validations_test : public beast::unit_test::Suite BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1); harness.vals().expire(j); BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 1); - harness.clock().advance(harness.parms().validationSET_EXPIRES); + harness.clock().advance(harness.parms().validationSetExpires); harness.vals().expire(j); BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerA.id()) == 0); @@ -666,14 +666,14 @@ class Validations_test : public beast::unit_test::Suite BEAST_EXPECT(ValStatus::Current == harness.add(a.validate(ledgerB))); BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1); harness.vals().setSeqToKeep(ledgerB.seq(), ledgerB.seq() + kOne); - harness.clock().advance(harness.parms().validationSET_EXPIRES); + harness.clock().advance(harness.parms().validationSetExpires); harness.vals().expire(j); BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerB.id()) == 1); // change toKeep harness.vals().setSeqToKeep(ledgerB.seq() + kOne, ledgerB.seq() + kTwo); // advance clock slowly int const loops = - harness.parms().validationSET_EXPIRES / harness.parms().validationFRESHNESS + 1; + harness.parms().validationSetExpires / harness.parms().validationFRESHNESS + 1; for (int i = 0; i < loops; ++i) { harness.clock().advance(harness.parms().validationFRESHNESS); @@ -686,7 +686,7 @@ class Validations_test : public beast::unit_test::Suite BEAST_EXPECT(ValStatus::Current == harness.add(a.validate(ledgerC))); BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 1); harness.vals().setSeqToKeep(ledgerC.seq() - kOne, ledgerC.seq()); - harness.clock().advance(harness.parms().validationSET_EXPIRES); + harness.clock().advance(harness.parms().validationSetExpires); harness.vals().expire(j); BEAST_EXPECT(harness.vals().numTrustedForLedger(ledgerC.id()) == 0); } @@ -934,7 +934,7 @@ class Validations_test : public beast::unit_test::Suite BEAST_EXPECT(enforcer(clock.now(), Seq{10}, p)); BEAST_EXPECT(!enforcer(clock.now(), Seq{5}, p)); BEAST_EXPECT(!enforcer(clock.now(), Seq{9}, p)); - clock.advance(p.validationSET_EXPIRES - 1ms); + clock.advance(p.validationSetExpires - 1ms); BEAST_EXPECT(!enforcer(clock.now(), Seq{1}, p)); clock.advance(2ms); BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p)); diff --git a/src/test/core/Config_test.cpp b/src/test/core/Config_test.cpp index 3e6495bc2f..ce6774827e 100644 --- a/src/test/core/Config_test.cpp +++ b/src/test/core/Config_test.cpp @@ -550,7 +550,7 @@ main } BEAST_EXPECT(error.empty()); - BEAST_EXPECT(c.NETWORK_ID == 0); + BEAST_EXPECT(c.networkId == 0); try { @@ -563,7 +563,7 @@ main } BEAST_EXPECT(error.empty()); - BEAST_EXPECT(c.NETWORK_ID == 0); + BEAST_EXPECT(c.networkId == 0); try { @@ -578,7 +578,7 @@ main } BEAST_EXPECT(error.empty()); - BEAST_EXPECT(c.NETWORK_ID == 255); + BEAST_EXPECT(c.networkId == 255); try { @@ -593,7 +593,7 @@ main } BEAST_EXPECT(error.empty()); - BEAST_EXPECT(c.NETWORK_ID == 10000); + BEAST_EXPECT(c.networkId == 10000); } void @@ -655,7 +655,7 @@ nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8 c.loadFromString(toLoad); BEAST_EXPECT(c.legacy("validators_file").empty()); BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 5); - BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == std::nullopt); + BEAST_EXPECT(c.validatorListThreshold == std::nullopt); } { // load validator list sites and keys from config @@ -685,7 +685,7 @@ trust-these-validators.gov "E566"); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values()[0] == "1"); - BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == std::size_t(1)); + BEAST_EXPECT(c.validatorListThreshold == std::size_t(1)); } { // load validator list sites and keys from config @@ -715,7 +715,7 @@ trust-these-validators.gov "E566"); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values()[0] == "0"); - BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == std::nullopt); + BEAST_EXPECT(c.validatorListThreshold == std::nullopt); } { // load should throw if [validator_list_threshold] is greater than @@ -836,7 +836,7 @@ trust-these-validators.gov BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); - BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2); + BEAST_EXPECT(c.validatorListThreshold == 2); } { // load from specified [validators_file] file name @@ -853,7 +853,7 @@ trust-these-validators.gov BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); - BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2); + BEAST_EXPECT(c.validatorListThreshold == 2); } { // load from specified [validators_file] relative path @@ -870,7 +870,7 @@ trust-these-validators.gov BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); - BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2); + BEAST_EXPECT(c.validatorListThreshold == 2); } { // load from validators file in default location @@ -885,7 +885,7 @@ trust-these-validators.gov BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); - BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2); + BEAST_EXPECT(c.validatorListThreshold == 2); } { // load from specified [validators_file] instead @@ -904,7 +904,7 @@ trust-these-validators.gov BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); - BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2); + BEAST_EXPECT(c.validatorListThreshold == 2); } { @@ -940,7 +940,7 @@ trust-these-validators.gov BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 4); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 3); BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); - BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2); + BEAST_EXPECT(c.validatorListThreshold == 2); } { // load should throw if [validator_list_threshold] is present both @@ -1017,7 +1017,7 @@ trust-these-validators.gov BEAST_EXPECT(!config.quiet()); BEAST_EXPECT(!config.silent()); BEAST_EXPECT(!config.standalone()); - BEAST_EXPECT(config.LEDGER_HISTORY == 256); + BEAST_EXPECT(config.ledgerHistory == 256); BEAST_EXPECT(!config.legacy("database_path").empty()); } { @@ -1030,7 +1030,7 @@ trust-these-validators.gov BEAST_EXPECT(config.quiet()); BEAST_EXPECT(!config.silent()); BEAST_EXPECT(!config.standalone()); - BEAST_EXPECT(config.LEDGER_HISTORY == 256); + BEAST_EXPECT(config.ledgerHistory == 256); BEAST_EXPECT(!config.legacy("database_path").empty()); } { @@ -1043,7 +1043,7 @@ trust-these-validators.gov BEAST_EXPECT(config.quiet()); BEAST_EXPECT(config.silent()); BEAST_EXPECT(!config.standalone()); - BEAST_EXPECT(config.LEDGER_HISTORY == 256); + BEAST_EXPECT(config.ledgerHistory == 256); BEAST_EXPECT(!config.legacy("database_path").empty()); } { @@ -1056,7 +1056,7 @@ trust-these-validators.gov BEAST_EXPECT(config.quiet()); BEAST_EXPECT(config.silent()); BEAST_EXPECT(!config.standalone()); - BEAST_EXPECT(config.LEDGER_HISTORY == 256); + BEAST_EXPECT(config.ledgerHistory == 256); BEAST_EXPECT(!config.legacy("database_path").empty()); } { @@ -1069,7 +1069,7 @@ trust-these-validators.gov BEAST_EXPECT(!config.quiet()); BEAST_EXPECT(!config.silent()); BEAST_EXPECT(config.standalone()); - BEAST_EXPECT(config.LEDGER_HISTORY == 0); + BEAST_EXPECT(config.ledgerHistory == 0); BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath); } { @@ -1082,7 +1082,7 @@ trust-these-validators.gov BEAST_EXPECT(config.quiet()); BEAST_EXPECT(!config.silent()); BEAST_EXPECT(config.standalone()); - BEAST_EXPECT(config.LEDGER_HISTORY == 0); + BEAST_EXPECT(config.ledgerHistory == 0); BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath); } { @@ -1095,7 +1095,7 @@ trust-these-validators.gov BEAST_EXPECT(config.quiet()); BEAST_EXPECT(config.silent()); BEAST_EXPECT(config.standalone()); - BEAST_EXPECT(config.LEDGER_HISTORY == 0); + BEAST_EXPECT(config.ledgerHistory == 0); BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath); } { @@ -1108,7 +1108,7 @@ trust-these-validators.gov BEAST_EXPECT(config.quiet()); BEAST_EXPECT(config.silent()); BEAST_EXPECT(config.standalone()); - BEAST_EXPECT(config.LEDGER_HISTORY == 0); + BEAST_EXPECT(config.ledgerHistory == 0); BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath); } } @@ -1125,11 +1125,11 @@ trust-these-validators.gov ParsedPort rpc; if (!unexcept([&]() { parsePort(rpc, conf["port_rpc"], log); })) return; - BEAST_EXPECT(rpc.admin_nets_v4.size() + rpc.admin_nets_v6.size() == 2); + BEAST_EXPECT(rpc.adminNetsV4.size() + rpc.adminNetsV6.size() == 2); ParsedPort wss; if (!unexcept([&]() { parsePort(wss, conf["port_wss_admin"], log); })) return; - BEAST_EXPECT(wss.admin_nets_v4.size() + wss.admin_nets_v6.size() == 1); + BEAST_EXPECT(wss.adminNetsV4.size() + wss.adminNetsV6.size() == 1); } void @@ -1245,25 +1245,25 @@ r.ripple.com:51235 BEAST_EXPECT( cfg.exists(SECTION_IPS_FIXED) && cfg.section(SECTION_IPS_FIXED).lines().size() == 15 && cfg.section(SECTION_IPS_FIXED).values().size() == 15); - BEAST_EXPECT(cfg.IPS[0] == "r.ripple.com 51235"); + BEAST_EXPECT(cfg.ips[0] == "r.ripple.com 51235"); - BEAST_EXPECT(cfg.IPS_FIXED[0] == "s1.ripple.com 51235"); - BEAST_EXPECT(cfg.IPS_FIXED[1] == "s2.ripple.com 51235"); - BEAST_EXPECT(cfg.IPS_FIXED[2] == "anotherserversansport"); - BEAST_EXPECT(cfg.IPS_FIXED[3] == "anotherserverwithport 12"); - BEAST_EXPECT(cfg.IPS_FIXED[4] == "1.1.1.1 1"); - BEAST_EXPECT(cfg.IPS_FIXED[5] == "1.1.1.1 1"); - BEAST_EXPECT(cfg.IPS_FIXED[6] == "12.34.12.123 12345"); - BEAST_EXPECT(cfg.IPS_FIXED[7] == "12.34.12.123 12345"); + BEAST_EXPECT(cfg.ipsFixed[0] == "s1.ripple.com 51235"); + BEAST_EXPECT(cfg.ipsFixed[1] == "s2.ripple.com 51235"); + BEAST_EXPECT(cfg.ipsFixed[2] == "anotherserversansport"); + BEAST_EXPECT(cfg.ipsFixed[3] == "anotherserverwithport 12"); + BEAST_EXPECT(cfg.ipsFixed[4] == "1.1.1.1 1"); + BEAST_EXPECT(cfg.ipsFixed[5] == "1.1.1.1 1"); + BEAST_EXPECT(cfg.ipsFixed[6] == "12.34.12.123 12345"); + BEAST_EXPECT(cfg.ipsFixed[7] == "12.34.12.123 12345"); // all ipv6 should be ignored by colon replacer, howsoever formatted - BEAST_EXPECT(cfg.IPS_FIXED[8] == "::"); - BEAST_EXPECT(cfg.IPS_FIXED[9] == "2001:db8::"); - BEAST_EXPECT(cfg.IPS_FIXED[10] == "::1"); - BEAST_EXPECT(cfg.IPS_FIXED[11] == "::1:12345"); - BEAST_EXPECT(cfg.IPS_FIXED[12] == "[::1]:12345"); - BEAST_EXPECT(cfg.IPS_FIXED[13] == "2001:db8:3333:4444:5555:6666:7777:8888:12345"); - BEAST_EXPECT(cfg.IPS_FIXED[14] == "[2001:db8:3333:4444:5555:6666:7777:8888]:1"); + BEAST_EXPECT(cfg.ipsFixed[8] == "::"); + BEAST_EXPECT(cfg.ipsFixed[9] == "2001:db8::"); + BEAST_EXPECT(cfg.ipsFixed[10] == "::1"); + BEAST_EXPECT(cfg.ipsFixed[11] == "::1:12345"); + BEAST_EXPECT(cfg.ipsFixed[12] == "[::1]:12345"); + BEAST_EXPECT(cfg.ipsFixed[13] == "2001:db8:3333:4444:5555:6666:7777:8888:12345"); + BEAST_EXPECT(cfg.ipsFixed[14] == "[2001:db8:3333:4444:5555:6666:7777:8888]:1"); } void @@ -1274,51 +1274,51 @@ r.ripple.com:51235 std::string_view line; std::string_view field; std::string_view expect; - bool had_comment; + bool hadComment; }; std::array const tests = { {{.line = "password = aaaa\\#bbbb", .field = "password", .expect = "aaaa#bbbb", - .had_comment = false}, + .hadComment = false}, {.line = "password = aaaa#bbbb", .field = "password", .expect = "aaaa", - .had_comment = true}, + .hadComment = true}, {.line = "password = aaaa #bbbb", .field = "password", .expect = "aaaa", - .had_comment = true}, + .hadComment = true}, // since the value is all comment, this doesn't parse as k=v : {.line = "password = #aaaa #bbbb", .field = "", .expect = "password =", - .had_comment = true}, + .hadComment = true}, {.line = "password = aaaa\\# #bbbb", .field = "password", .expect = "aaaa#", - .had_comment = true}, + .hadComment = true}, {.line = "password = aaaa\\##bbbb", .field = "password", .expect = "aaaa#", - .had_comment = true}, - {.line = "aaaa#bbbb", .field = "", .expect = "aaaa", .had_comment = true}, - {.line = "aaaa\\#bbbb", .field = "", .expect = "aaaa#bbbb", .had_comment = false}, - {.line = "aaaa\\##bbbb", .field = "", .expect = "aaaa#", .had_comment = true}, - {.line = "aaaa #bbbb", .field = "", .expect = "aaaa", .had_comment = true}, - {.line = "1 #comment", .field = "", .expect = "1", .had_comment = true}, - {.line = "#whole thing is comment", .field = "", .expect = "", .had_comment = false}, + .hadComment = true}, + {.line = "aaaa#bbbb", .field = "", .expect = "aaaa", .hadComment = true}, + {.line = "aaaa\\#bbbb", .field = "", .expect = "aaaa#bbbb", .hadComment = false}, + {.line = "aaaa\\##bbbb", .field = "", .expect = "aaaa#", .hadComment = true}, + {.line = "aaaa #bbbb", .field = "", .expect = "aaaa", .hadComment = true}, + {.line = "1 #comment", .field = "", .expect = "1", .hadComment = true}, + {.line = "#whole thing is comment", .field = "", .expect = "", .hadComment = false}, {.line = " #whole comment with space", .field = "", .expect = "", - .had_comment = false}}}; + .hadComment = false}}}; for (auto const& t : tests) { Section s; s.append(std::string(t.line)); - BEAST_EXPECT(s.hadTrailingComments() == t.had_comment); + BEAST_EXPECT(s.hadTrailingComments() == t.hadComment); if (t.field.empty()) { BEAST_EXPECTS(s.legacy() == t.expect, s.legacy()); @@ -1481,7 +1481,7 @@ r.ripple.com:51235 c.loadFromString(toLoad); if (shouldPass) { - BEAST_EXPECT(c.AMENDMENT_MAJORITY_TIME.count() == val * sec); + BEAST_EXPECT(c.amendmentMajorityTime.count() == val * sec); } else { @@ -1512,7 +1512,7 @@ r.ripple.com:51235 { Config c; c.loadFromString("[overlay]\nmax_unknown_time=" + value); - return c.MAX_UNKNOWN_TIME; + return c.maxUnknownTime; } catch (std::runtime_error const&) { @@ -1546,7 +1546,7 @@ r.ripple.com:51235 { Config c; c.loadFromString("[overlay]\nmax_diverged_time=" + value); - return c.MAX_DIVERGED_TIME; + return c.maxDivergedTime; } catch (std::runtime_error const&) { diff --git a/src/test/core/Coroutine_test.cpp b/src/test/core/Coroutine_test.cpp index 103191ec59..bf1d98c779 100644 --- a/src/test/core/Coroutine_test.cpp +++ b/src/test/core/Coroutine_test.cpp @@ -58,7 +58,7 @@ public: testcase("correct order"); Env env(*this, envconfig([](std::unique_ptr cfg) { - cfg->FORCE_MULTI_THREAD = true; + cfg->forceMultiThread = true; return cfg; })); @@ -85,7 +85,7 @@ public: testcase("incorrect order"); Env env(*this, envconfig([](std::unique_ptr cfg) { - cfg->FORCE_MULTI_THREAD = true; + cfg->forceMultiThread = true; return cfg; })); diff --git a/src/test/csf/Scheduler.h b/src/test/csf/Scheduler.h index c70b7d4854..ede43be854 100644 --- a/src/test/csf/Scheduler.h +++ b/src/test/csf/Scheduler.h @@ -91,7 +91,7 @@ private: make_multiset>::type; // alloc_ is owned by the scheduler boost::container::pmr::monotonic_buffer_resource* alloc_; - by_when_set by_when_; + by_when_set byWhen_; public: using iterator = typename by_when_set::iterator; @@ -258,7 +258,7 @@ inline Scheduler::QueueType::QueueType(boost::container::pmr::monotonic_buffer_r inline Scheduler::QueueType::~QueueType() { - for (auto iter = by_when_.begin(); iter != by_when_.end();) + for (auto iter = byWhen_.begin(); iter != byWhen_.end();) { auto e = &*iter; ++iter; @@ -270,19 +270,19 @@ inline Scheduler::QueueType::~QueueType() inline bool Scheduler::QueueType::empty() const { - return by_when_.empty(); + return byWhen_.empty(); } inline auto Scheduler::QueueType::begin() -> iterator { - return by_when_.begin(); + return byWhen_.begin(); } inline auto Scheduler::QueueType::end() -> iterator { - return by_when_.end(); + return byWhen_.end(); } template @@ -292,14 +292,14 @@ Scheduler::QueueType::emplace(time_point when, Handler&& h) -> typename by_when_ using event_type = EventImpl>; auto const p = alloc_->allocate(sizeof(event_type)); auto& e = *new (p) event_type(when, std::forward(h)); - return by_when_.insert(e); + return byWhen_.insert(e); } inline auto Scheduler::QueueType::erase(iterator iter) -> typename by_when_set::iterator { auto& e = *iter; - auto next = by_when_.erase(iter); + auto next = byWhen_.erase(iter); e.~Event(); alloc_->deallocate(&e, sizeof(e)); return next; diff --git a/src/test/jtx/impl/AMMTest.cpp b/src/test/jtx/impl/AMMTest.cpp index 5801e8fb44..46c5e44bd3 100644 --- a/src/test/jtx/impl/AMMTest.cpp +++ b/src/test/jtx/impl/AMMTest.cpp @@ -213,9 +213,9 @@ AMMTest::pathTestEnv() // different from the current defaults. This function creates an env // with the search parameters that the tests were written for. return Env(*this, envconfig([](std::unique_ptr cfg) { - cfg->PATH_SEARCH_OLD = 7; - cfg->PATH_SEARCH = 7; - cfg->PATH_SEARCH_MAX = 10; + cfg->pathSearchOld = 7; + cfg->pathSearch = 7; + cfg->pathSearchMax = 10; return cfg; })); } diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 1cd4dd1029..4b6955bafb 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -91,7 +91,7 @@ Env::AppBundle::AppBundle( timeKeeper = tk.get(); // Hack so we don't have to call Config::setup HTTPClient::initializeSSLContext( - config->SSL_VERIFY_DIR, config->SSL_VERIFY_FILE, config->SSL_VERIFY, debugLog()); + config->sslVerifyDir, config->sslVerifyFile, config->sslVerify, debugLog()); owned = makeApplication(std::move(config), std::move(logs), std::move(tk)); app = owned.get(); app->getLogs().threshold(thresh); diff --git a/src/test/jtx/impl/JSONRPCClient.cpp b/src/test/jtx/impl/JSONRPCClient.cpp index c8e6326ed2..cf81bfab0c 100644 --- a/src/test/jtx/impl/JSONRPCClient.cpp +++ b/src/test/jtx/impl/JSONRPCClient.cpp @@ -81,11 +81,11 @@ class JSONRPCClient : public AbstractClient boost::asio::ip::tcp::socket stream_; boost::beast::multi_buffer bin_; boost::beast::multi_buffer bout_; - unsigned rpc_version_; + unsigned rpcVersion_; public: explicit JSONRPCClient(Config const& cfg, unsigned rpcVersion) - : ep_(getEndpoint(cfg)), stream_(ios_), rpc_version_(rpcVersion) + : ep_(getEndpoint(cfg)), stream_(ios_), rpcVersion_(rpcVersion) { stream_.connect(ep_); } @@ -116,7 +116,7 @@ public: { json::Value jr; jr[jss::method] = cmd; - if (rpc_version_ == 2) + if (rpcVersion_ == 2) { jr[jss::jsonrpc] = "2.0"; jr[jss::ripplerpc] = "2.0"; @@ -148,7 +148,7 @@ public: [[nodiscard]] unsigned version() const override { - return rpc_version_; + return rpcVersion_; } }; diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index e9ea48fb39..c784c074de 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -172,9 +172,9 @@ pathTestEnv(beast::unit_test::Suite& suite) // with the search parameters that the tests were written for. using namespace jtx; return Env(suite, envconfig([](std::unique_ptr cfg) { - cfg->PATH_SEARCH_OLD = 7; - cfg->PATH_SEARCH = 7; - cfg->PATH_SEARCH_MAX = 10; + cfg->pathSearchOld = 7; + cfg->pathSearch = 7; + cfg->pathSearchMax = 10; return cfg; })); } diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index ba09647ce7..551fd1404b 100644 --- a/src/test/jtx/impl/WSClient.cpp +++ b/src/test/jtx/impl/WSClient.cpp @@ -120,7 +120,7 @@ class WSClientImpl : public WSClient std::condition_variable cv_; std::list> msgs_; - unsigned rpc_version_; + unsigned rpcVersion_; void cleanup() @@ -157,7 +157,7 @@ public: , thread_([&] { ios_.run(); }) , stream_(ios_) , ws_(stream_) - , rpc_version_(rpcVersion) + , rpcVersion_(rpcVersion) { try { @@ -197,7 +197,7 @@ public: json::Value jp; if (params) jp = params; - if (rpc_version_ == 2) + if (rpcVersion_ == 2) { jp[jss::method] = cmd; jp[jss::jsonrpc] = "2.0"; @@ -284,7 +284,7 @@ public: [[nodiscard]] unsigned version() const override { - return rpc_version_; + return rpcVersion_; } private: diff --git a/src/test/jtx/impl/envconfig.cpp b/src/test/jtx/impl/envconfig.cpp index 648f8e4460..56197e1078 100644 --- a/src/test/jtx/impl/envconfig.cpp +++ b/src/test/jtx/impl/envconfig.cpp @@ -20,12 +20,12 @@ setupConfigForUnitTests(Config& cfg) using namespace jtx; // Default fees to old values, so tests don't have to worry about changes in // Config.h - cfg.FEES.reference_fee = UNIT_TEST_REFERENCE_FEE; - cfg.FEES.account_reserve = XRP(200).value().xrp().drops(); - cfg.FEES.owner_reserve = XRP(50).value().xrp().drops(); + cfg.fees.referenceFee = UNIT_TEST_REFERENCE_FEE; + cfg.fees.accountReserve = XRP(200).value().xrp().drops(); + cfg.fees.ownerReserve = XRP(50).value().xrp().drops(); // The Beta API (currently v2) is always available to tests - cfg.BETA_RPC_API = true; + cfg.betaRpcApi = true; cfg.overwrite(ConfigSection::nodeDatabase(), "type", "memory"); cfg.overwrite(ConfigSection::nodeDatabase(), "path", "main"); @@ -54,7 +54,7 @@ setupConfigForUnitTests(Config& cfg) cfg[PORT_WS].set("admin", getEnvLocalhostAddr()); cfg[PORT_WS].set("port", "0"); cfg[PORT_WS].set("protocol", "ws"); - cfg.SSL_VERIFY = false; + cfg.sslVerify = false; } namespace jtx { @@ -96,7 +96,7 @@ secureGatewayLocalnet(std::unique_ptr cfg) std::unique_ptr singleThreadIo(std::unique_ptr cfg) { - cfg->IO_WORKERS = 1; + cfg->ioWorkers = 1; return cfg; } diff --git a/src/test/jtx/impl/permissioned_dex.cpp b/src/test/jtx/impl/permissioned_dex.cpp index 012932fed5..5c059e9e80 100644 --- a/src/test/jtx/impl/permissioned_dex.cpp +++ b/src/test/jtx/impl/permissioned_dex.cpp @@ -48,7 +48,7 @@ PermissionedDEX::PermissionedDEX(Env& env) , alice("permdex-alice") , bob("permdex-bob") , carol("permdex-carol") - , USD(gw["USD"]) + , usd(gw["USD"]) , credType("permdex-abcde") { // Fund accounts @@ -59,10 +59,10 @@ PermissionedDEX::PermissionedDEX(Env& env) for (auto const& account : {alice, bob, carol, domainOwner}) { - env.trust(USD(1000), account); + env.trust(usd(1000), account); env.close(); - env(pay(gw, account, USD(100))); + env(pay(gw, account, usd(100))); env.close(); } } diff --git a/src/test/jtx/impl/xchain_bridge.cpp b/src/test/jtx/impl/xchain_bridge.cpp index a88535a51e..24669e6d4f 100644 --- a/src/test/jtx/impl/xchain_bridge.cpp +++ b/src/test/jtx/impl/xchain_bridge.cpp @@ -396,7 +396,7 @@ XChainBridgeObjects::XChainBridgeObjects() } return result; }()) - , alt_signers([] { + , altSigners([] { static constexpr int kNumSigners = kUtXchainDefaultNumSigners; std::vector result; result.reserve(kNumSigners); @@ -431,17 +431,16 @@ XChainBridgeObjects::XChainBridgeObjects() return r; }()) , reward(XRP(1)) - , split_reward_quorum(divide(reward, STAmount(kUtXchainDefaultQuorum), reward.get())) - , split_reward_everyone( - divide(reward, STAmount(kUtXchainDefaultNumSigners), reward.get())) - , tiny_reward(drops(37)) - , tiny_reward_split( - (divide(tiny_reward, STAmount(kUtXchainDefaultQuorum), tiny_reward.get()))) - , tiny_reward_remainder( - tiny_reward - - multiply(tiny_reward_split, STAmount(kUtXchainDefaultQuorum), tiny_reward.get())) - , one_xrp(XRP(1)) - , xrp_dust(divide(one_xrp, STAmount(10000), one_xrp.get())) + , splitRewardQuorum(divide(reward, STAmount(kUtXchainDefaultQuorum), reward.get())) + , splitRewardEveryone(divide(reward, STAmount(kUtXchainDefaultNumSigners), reward.get())) + , tinyReward(drops(37)) + , tinyRewardSplit( + (divide(tinyReward, STAmount(kUtXchainDefaultQuorum), tinyReward.get()))) + , tinyRewardRemainder( + tinyReward - + multiply(tinyRewardSplit, STAmount(kUtXchainDefaultQuorum), tinyReward.get())) + , oneXrp(XRP(1)) + , xrpDust(divide(oneXrp, STAmount(10000), oneXrp.get())) { } diff --git a/src/test/jtx/permissioned_dex.h b/src/test/jtx/permissioned_dex.h index 025097116f..b0a4d4e2d5 100644 --- a/src/test/jtx/permissioned_dex.h +++ b/src/test/jtx/permissioned_dex.h @@ -20,7 +20,7 @@ public: Account alice; Account bob; Account carol; - IOU USD; + IOU usd; uint256 domainID; std::string credType; diff --git a/src/test/jtx/xchain_bridge.h b/src/test/jtx/xchain_bridge.h index c20b6f2fc5..e4c012510d 100644 --- a/src/test/jtx/xchain_bridge.h +++ b/src/test/jtx/xchain_bridge.h @@ -165,21 +165,21 @@ struct XChainBridgeObjects FeatureBitset const features; std::vector const signers; - std::vector const alt_signers; + std::vector const altSigners; std::vector const payee; std::vector const payees; std::uint32_t const quorum{kUtXchainDefaultQuorum}; - STAmount const reward; // 1 xrp - STAmount const split_reward_quorum; // 250,000 drops - STAmount const split_reward_everyone; // 200,000 drops + STAmount const reward; // 1 xrp + STAmount const splitRewardQuorum; // 250,000 drops + STAmount const splitRewardEveryone; // 200,000 drops - STAmount const tiny_reward; // 37 drops - STAmount const tiny_reward_split; // 9 drops - STAmount const tiny_reward_remainder; // 1 drops + STAmount const tinyReward; // 37 drops + STAmount const tinyRewardSplit; // 9 drops + STAmount const tinyRewardRemainder; // 1 drops - STAmount const one_xrp; - STAmount const xrp_dust; + STAmount const oneXrp; + STAmount const xrpDust; static constexpr int kDropPerXrp = 1000000; diff --git a/src/test/ledger/SkipList_test.cpp b/src/test/ledger/SkipList_test.cpp index fe671ed128..fd3cc2f214 100644 --- a/src/test/ledger/SkipList_test.cpp +++ b/src/test/ledger/SkipList_test.cpp @@ -25,7 +25,7 @@ class SkipList_test : public beast::unit_test::Suite auto prev = std::make_shared( kCreateGenesis, Rules{config.features}, - config.FEES.toFees(), + config.fees.toFees(), std::vector{}, env.app().getNodeFamily()); history.push_back(prev); diff --git a/src/test/ledger/View_test.cpp b/src/test/ledger/View_test.cpp index 1714702c18..d1c7316588 100644 --- a/src/test/ledger/View_test.cpp +++ b/src/test/ledger/View_test.cpp @@ -153,7 +153,7 @@ class View_test : public beast::unit_test::Suite std::shared_ptr const genesis = std::make_shared( kCreateGenesis, Rules{config.features}, - config.FEES.toFees(), + config.fees.toFees(), std::vector{}, env.app().getNodeFamily()); auto const ledger = @@ -421,7 +421,7 @@ class View_test : public beast::unit_test::Suite std::shared_ptr const genesis = std::make_shared( kCreateGenesis, Rules{config.features}, - config.FEES.toFees(), + config.fees.toFees(), std::vector{}, env.app().getNodeFamily()); auto const ledger = @@ -630,7 +630,7 @@ class View_test : public beast::unit_test::Suite std::shared_ptr const genesis = std::make_shared( kCreateGenesis, Rules{config.features}, - config.FEES.toFees(), + config.fees.toFees(), std::vector{}, env.app().getNodeFamily()); auto const ledger = @@ -1020,7 +1020,7 @@ class View_test : public beast::unit_test::Suite std::shared_ptr const genesis = std::make_shared( kCreateGenesis, Rules{config.features}, - config.FEES.toFees(), + config.fees.toFees(), std::vector{}, env.app().getNodeFamily()); auto const ledger = diff --git a/src/test/nodestore/Database_test.cpp b/src/test/nodestore/Database_test.cpp index 744717659a..50bdfefd4c 100644 --- a/src/test/nodestore/Database_test.cpp +++ b/src/test/nodestore/Database_test.cpp @@ -74,7 +74,7 @@ public: auto& section = p->section("sqlite"); section.set("safety_level", "high"); } - p->LEDGER_HISTORY = 100'000'000; + p->ledgerHistory = 100'000'000; return Env( *this, @@ -103,7 +103,7 @@ public: auto& section = p->section("sqlite"); section.set("safety_level", "low"); } - p->LEDGER_HISTORY = 100'000'000; + p->ledgerHistory = 100'000'000; return Env( *this, @@ -166,7 +166,7 @@ public: section.set("synchronous", "extra"); section.set("temp_store", "default"); } - p->LEDGER_HISTORY = 50'000'000; + p->ledgerHistory = 50'000'000; return Env( *this, diff --git a/src/test/nodestore/Timing_test.cpp b/src/test/nodestore/Timing_test.cpp index d7540f0b3a..67feace198 100644 --- a/src/test/nodestore/Timing_test.cpp +++ b/src/test/nodestore/Timing_test.cpp @@ -84,16 +84,16 @@ private: beast::xor_shift_engine gen_; std::uint8_t prefix_; - std::discrete_distribution d_type_; - std::uniform_int_distribution d_size_; + std::discrete_distribution dType_; + std::uniform_int_distribution dSize_; public: explicit Sequence(std::uint8_t prefix) : prefix_(prefix) // uniform distribution over hotLEDGER - hotTRANSACTION_NODE // but exclude hotTRANSACTION = 2 (removed) - , d_type_({1, 1, 0, 1, 1}) - , d_size_(kMinSize, kMaxSize) + , dType_({1, 1, 0, 1, 1}) + , dSize_(kMinSize, kMaxSize) { } @@ -116,10 +116,10 @@ public: auto const data = static_cast(&*key.begin()); *data = prefix_; rngcpy(data + 1, key.size() - 1, gen_); - Blob value(d_size_(gen_)); + Blob value(dSize_(gen_)); rngcpy(&value[0], value.size(), gen_); return NodeObject::createObject( - safeCast(d_type_(gen_)), std::move(value), key); + safeCast(dType_(gen_)), std::move(value), key); } // returns a batch of NodeObjects starting at n @@ -140,11 +140,11 @@ class Timing_test : public beast::unit_test::Suite public: static constexpr auto kMissingNodePercent = 20; // percent of fetches for missing nodes - std::size_t const default_repeat = 3; + std::size_t const defaultRepeat = 3; #ifndef NDEBUG - std::size_t const default_items = 10000; + std::size_t const defaultItems = 10000; #else - std::size_t const default_items = 100000; // release + std::size_t const defaultItems = 100000; // release #endif using clock_type = std::chrono::steady_clock; @@ -639,7 +639,7 @@ public: { w = std::max::size_type>(w, test.first.size()); } - log << threads << " Thread" << (threads > 1 ? "s" : "") << ", " << default_items + log << threads << " Thread" << (threads > 1 ? "s" : "") << ", " << defaultItems << " Objects" << std::endl; { std::stringstream ss; @@ -655,9 +655,9 @@ public: for (auto const& configString : configStrings) { Params params{}; - params.items = default_items; + params.items = defaultItems; params.threads = threads; - for (auto i = default_repeat; (i--) != 0u;) + for (auto i = defaultRepeat; (i--) != 0u;) { beast::TempDir const tempDir; Section config = parse(configString); diff --git a/src/test/overlay/compression_test.cpp b/src/test/overlay/compression_test.cpp index d98ffa9b33..60cc69a14f 100644 --- a/src/test/overlay/compression_test.cpp +++ b/src/test/overlay/compression_test.cpp @@ -112,16 +112,16 @@ public: return; std::vector decompressed; - decompressed.resize(header->uncompressed_size); + decompressed.resize(header->uncompressedSize); - BEAST_EXPECT(header->payload_wire_size == buffer.size() - header->header_size); + BEAST_EXPECT(header->payloadWireSize == buffer.size() - header->headerSize); ZeroCopyInputStream stream(buffers.data()); - stream.Skip(header->header_size); + stream.Skip(header->headerSize); auto decompressedSize = xrpl::compression::decompress( - stream, header->payload_wire_size, decompressed.data(), header->uncompressed_size); - BEAST_EXPECT(decompressedSize == header->uncompressed_size); + stream, header->payloadWireSize, decompressed.data(), header->uncompressedSize); + BEAST_EXPECT(decompressedSize == header->uncompressedSize); auto const proto1 = std::make_shared(); BEAST_EXPECT(proto1->ParseFromArray(decompressed.data(), decompressedSize)); @@ -408,9 +408,8 @@ public: << enable << "\n"; c.loadFromString(str.str()); auto env = std::make_shared(*this); - env->app().config().COMPRESSION = c.COMPRESSION; - env->app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = - c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE; + env->app().config().compression = c.compression; + env->app().config().vpReduceRelayBaseSquelchEnable = c.vpReduceRelayBaseSquelchEnable; return env; }; auto handshake = [&](int outboundEnable, int inboundEnable) { @@ -419,10 +418,10 @@ public: auto env = getEnv(outboundEnable); auto request = xrpl::makeRequest( true, - env->app().config().COMPRESSION, + env->app().config().compression, false, - env->app().config().TX_REDUCE_RELAY_ENABLE, - env->app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE); + env->app().config().txReduceRelayEnable, + env->app().config().vpReduceRelayBaseSquelchEnable); http_request_type httpRequest; httpRequest.version(request.version()); httpRequest.base() = request.base(); diff --git a/src/test/overlay/reduce_relay_test.cpp b/src/test/overlay/reduce_relay_test.cpp index a0afd01d2a..54d8f555a2 100644 --- a/src/test/overlay/reduce_relay_test.cpp +++ b/src/test/overlay/reduce_relay_test.cpp @@ -973,8 +973,7 @@ protected: auto countingState = network_.overlay().isCountingState(validator); BEAST_EXPECT( countingState == false && - selected.size() == - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + selected.size() == env_.app().config().vpReduceRelaySquelchMaxSelectedPeers); } // Trigger Link Down or Peer Disconnect event @@ -1163,7 +1162,7 @@ protected: { BEAST_EXPECT( squelched == - kMaxPeers - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + kMaxPeers - env_.app().config().vpReduceRelaySquelchMaxSelectedPeers); n++; } }, @@ -1172,8 +1171,7 @@ protected: purge, resetClock); auto selected = network_.overlay().getSelected(network_.validator(0)); - BEAST_EXPECT( - selected.size() == env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + BEAST_EXPECT(selected.size() == env_.app().config().vpReduceRelaySquelchMaxSelectedPeers); BEAST_EXPECT(n == 1); // only one selection round auto res = checkCounting(network_.validator(0), false); BEAST_EXPECT(res); @@ -1234,7 +1232,7 @@ protected: id, [&](PublicKey const& key, PeerWPtr const& peer) { unsquelched++; }); BEAST_EXPECT( unsquelched == - kMaxPeers - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + kMaxPeers - env_.app().config().vpReduceRelaySquelchMaxSelectedPeers); BEAST_EXPECT(checkCounting(network_.validator(0), true)); }); } @@ -1254,7 +1252,7 @@ protected: auto peers = network_.overlay().getPeers(network_.validator(0)); BEAST_EXPECT( unsquelched == - kMaxPeers - env_.app().config().VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS); + kMaxPeers - env_.app().config().vpReduceRelaySquelchMaxSelectedPeers); BEAST_EXPECT(checkCounting(network_.validator(0), true)); }); } @@ -1293,7 +1291,7 @@ vp_enable=1 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == true); + BEAST_EXPECT(c.vpReduceRelayBaseSquelchEnable == true); }); doTest("Test Config - squelch disabled (legacy)", log, [&](bool log) { @@ -1305,7 +1303,7 @@ vp_enable=0 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == false); + BEAST_EXPECT(c.vpReduceRelayBaseSquelchEnable == false); Config c1; @@ -1314,7 +1312,7 @@ vp_enable=0 )xrpldConfig"; c1.loadFromString(toLoad); - BEAST_EXPECT(c1.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == false); + BEAST_EXPECT(c1.vpReduceRelayBaseSquelchEnable == false); }); doTest("Test Config - squelch enabled", log, [&](bool log) { @@ -1326,7 +1324,7 @@ vp_base_squelch_enable=1 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == true); + BEAST_EXPECT(c.vpReduceRelayBaseSquelchEnable == true); }); doTest("Test Config - squelch disabled", log, [&](bool log) { @@ -1338,7 +1336,7 @@ vp_base_squelch_enable=0 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE == false); + BEAST_EXPECT(c.vpReduceRelayBaseSquelchEnable == false); }); doTest("Test Config - legacy and new", log, [&](bool log) { @@ -1378,7 +1376,7 @@ vp_enable=0 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS == 5); + BEAST_EXPECT(c.vpReduceRelaySquelchMaxSelectedPeers == 5); Config c1; @@ -1388,7 +1386,7 @@ vp_base_squelch_max_selected_peers=6 )xrpldConfig"; c1.loadFromString(toLoad); - BEAST_EXPECT(c1.VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS == 6); + BEAST_EXPECT(c1.vpReduceRelaySquelchMaxSelectedPeers == 6); Config c2; @@ -1421,7 +1419,7 @@ vp_base_squelch_max_selected_peers=2 doTest("BaseSquelchReady", log, [&](bool log) { ManualClock::reset(); auto createSlots = [&](bool baseSquelchEnabled) -> reduce_relay::Slots { - env_.app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = baseSquelchEnabled; + env_.app().config().vpReduceRelayBaseSquelchEnable = baseSquelchEnabled; return reduce_relay::Slots( env_.app(), network_.overlay(), env_.app().config()); }; @@ -1577,10 +1575,10 @@ vp_base_squelch_max_selected_peers=2 << "[compression]\n" << "1\n"; c.loadFromString(str.str()); - env_.app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = - c.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE; + env_.app().config().vpReduceRelayBaseSquelchEnable = + c.vpReduceRelayBaseSquelchEnable; - env_.app().config().COMPRESSION = c.COMPRESSION; + env_.app().config().compression = c.compression; }; auto handshake = [&](int outboundEnable, int inboundEnable) { beast::IP::Address const addr = boost::asio::ip::make_address("172.1.1.100"); @@ -1588,10 +1586,10 @@ vp_base_squelch_max_selected_peers=2 setEnv(outboundEnable); auto request = xrpl::makeRequest( true, - env_.app().config().COMPRESSION, + env_.app().config().compression, false, - env_.app().config().TX_REDUCE_RELAY_ENABLE, - env_.app().config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE); + env_.app().config().txReduceRelayEnable, + env_.app().config().vpReduceRelayBaseSquelchEnable); http_request_type httpRequest; httpRequest.version(request.version()); httpRequest.base() = request.base(); @@ -1626,8 +1624,8 @@ vp_base_squelch_max_selected_peers=2 public: reduce_relay_test() : env_(*this, jtx::envconfig([](std::unique_ptr cfg) { - cfg->VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = true; - cfg->VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS = 6; + cfg->vpReduceRelayBaseSquelchEnable = true; + cfg->vpReduceRelaySquelchMaxSelectedPeers = 6; return cfg; })) , network_(env_.app()) diff --git a/src/test/overlay/short_read_test.cpp b/src/test/overlay/short_read_test.cpp index 95ab5e8c20..9af632d65c 100644 --- a/src/test/overlay/short_read_test.cpp +++ b/src/test/overlay/short_read_test.cpp @@ -62,7 +62,7 @@ private: using endpoint_type = boost::asio::ip::tcp::endpoint; using address_type = boost::asio::ip::address; - io_context_type io_context_; + io_context_type ioContext_; boost::optional> work_; std::thread thread_; std::shared_ptr context_; @@ -185,10 +185,10 @@ private: , server(server) , test(server.test_) , acceptor( - test.io_context_, + test.ioContext_, endpoint_type(boost::asio::ip::make_address(test::getEnvLocalhostAddr()), 0)) - , socket(test.io_context_) - , strand(boost::asio::make_strand(test.io_context_)) + , socket(test.ioContext_) + , strand(boost::asio::make_strand(test.ioContext_)) { acceptor.listen(); server.endpoint_ = acceptor.local_endpoint(); @@ -261,8 +261,8 @@ private: , test(server.test_) , socket(std::move(inSocket)) , stream(socket, *test.context_) - , strand(boost::asio::make_strand(test.io_context_)) - , timer(test.io_context_) + , strand(boost::asio::make_strand(test.ioContext_)) + , timer(test.ioContext_) { } @@ -450,10 +450,10 @@ private: : Child(client) , client(client) , test(client.test_) - , socket(test.io_context_) + , socket(test.ioContext_) , stream(socket, *test.context_) - , strand(boost::asio::make_strand(test.io_context_)) - , timer(test.io_context_) + , strand(boost::asio::make_strand(test.ioContext_)) + , timer(test.ioContext_) , ep(ep) { } @@ -632,10 +632,10 @@ private: public: short_read_test() - : work_(io_context_.get_executor()) + : work_(ioContext_.get_executor()) , thread_(std::thread([this]() { beast::setCurrentThreadName("io_context"); - this->io_context_.run(); + this->ioContext_.run(); })) , context_(makeSslContext("")) { diff --git a/src/test/overlay/tx_reduce_relay_test.cpp b/src/test/overlay/tx_reduce_relay_test.cpp index e053774108..a0d91d3aed 100644 --- a/src/test/overlay/tx_reduce_relay_test.cpp +++ b/src/test/overlay/tx_reduce_relay_test.cpp @@ -81,10 +81,10 @@ private: { c.loadFromString(str.str()); - BEAST_EXPECT(c.TX_REDUCE_RELAY_ENABLE == enable); - BEAST_EXPECT(c.TX_REDUCE_RELAY_METRICS == metrics); - BEAST_EXPECT(c.TX_REDUCE_RELAY_MIN_PEERS == min); - BEAST_EXPECT(c.TX_RELAY_PERCENTAGE == pct); + BEAST_EXPECT(c.txReduceRelayEnable == enable); + BEAST_EXPECT(c.txReduceRelayMetrics == metrics); + BEAST_EXPECT(c.txReduceRelayMinPeers == min); + BEAST_EXPECT(c.txRelayPercentage == pct); if (success) { pass(); @@ -173,7 +173,7 @@ private: std::uint16_t rid_{1}; shared_context context_; ProtocolVersion protocolVersion_; - boost::beast::multi_buffer read_buf_; + boost::beast::multi_buffer readBuf_; public: tx_reduce_relay_test() : context_(makeSslContext("")), protocolVersion_{1, 7} @@ -232,9 +232,9 @@ private: testcase(test); jtx::Env env(*this); std::vector> peers; - env.app().config().TX_REDUCE_RELAY_ENABLE = txRREnabled; - env.app().config().TX_REDUCE_RELAY_MIN_PEERS = minPeers; - env.app().config().TX_RELAY_PERCENTAGE = relayPercentage; + env.app().config().txReduceRelayEnable = txRREnabled; + env.app().config().txReduceRelayMinPeers = minPeers; + env.app().config().txRelayPercentage = relayPercentage; PeerTest::init(); lid_ = 0; rid_ = 0; diff --git a/src/test/peerfinder/PeerFinder_test.cpp b/src/test/peerfinder/PeerFinder_test.cpp index 2e4ab001b4..c4f129c1a3 100644 --- a/src/test/peerfinder/PeerFinder_test.cpp +++ b/src/test/peerfinder/PeerFinder_test.cpp @@ -421,8 +421,8 @@ public: c.loadFromString(toLoad); BEAST_EXPECT( - (c.PEERS_MAX == max && c.PEERS_IN_MAX == 0 && c.PEERS_OUT_MAX == 0) || - (c.PEERS_IN_MAX == *maxIn && c.PEERS_OUT_MAX == *maxOut)); + (c.peersMax == max && c.peersInMax == 0 && c.peersOutMax == 0) || + (c.peersInMax == *maxIn && c.peersOutMax == *maxOut)); Config const config = Config::makeConfig(c, port, false, 0, true); diff --git a/src/test/rpc/AccountTx_test.cpp b/src/test/rpc/AccountTx_test.cpp index 5d32eac8dc..421f6bd1fa 100644 --- a/src/test/rpc/AccountTx_test.cpp +++ b/src/test/rpc/AccountTx_test.cpp @@ -131,7 +131,7 @@ class AccountTx_test : public beast::unit_test::Suite using namespace test::jtx; Env env(*this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; return cfg; })); Account const a1{"A1"}; @@ -821,7 +821,7 @@ class AccountTx_test : public beast::unit_test::Suite using namespace std::chrono_literals; auto cfg = makeConfig(); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env(*this, std::move(cfg)); Account const alice{"alice"}; diff --git a/src/test/rpc/Book_test.cpp b/src/test/rpc/Book_test.cpp index bde55d4985..83f7b64b4b 100644 --- a/src/test/rpc/Book_test.cpp +++ b/src/test/rpc/Book_test.cpp @@ -1593,7 +1593,7 @@ public: auto const carol = permDex.carol; auto const domainID = permDex.domainID; auto const gw = permDex.gw; - auto const usd = permDex.USD; + auto const usd = permDex.usd; auto wsc = makeWSClient(env.app().config()); @@ -1718,7 +1718,7 @@ public: auto const carol = permDex.carol; auto const domainID = permDex.domainID; auto const gw = permDex.gw; - auto const usd = permDex.USD; + auto const usd = permDex.usd; auto wsc = makeWSClient(env.app().config()); diff --git a/src/test/rpc/DeliveredAmount_test.cpp b/src/test/rpc/DeliveredAmount_test.cpp index 7daf05aaf1..ca5df7102e 100644 --- a/src/test/rpc/DeliveredAmount_test.cpp +++ b/src/test/rpc/DeliveredAmount_test.cpp @@ -206,7 +206,7 @@ class DeliveredAmount_test : public beast::unit_test::Suite for (bool const afterSwitchTime : {true, false}) { auto cfg = envconfig(); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env(*this, std::move(cfg)); env.fund(XRP(10000), alice, bob, carol, gw); env.trust(usd(1000), alice, bob, carol); @@ -297,7 +297,7 @@ class DeliveredAmount_test : public beast::unit_test::Suite for (bool const afterSwitchTime : {true, false}) { auto cfg = envconfig(); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env(*this, std::move(cfg)); env.fund(XRP(10000), alice, bob, carol, gw); env.trust(usd(1000), alice, bob, carol); diff --git a/src/test/rpc/JSONRPC_test.cpp b/src/test/rpc/JSONRPC_test.cpp index 10e4daf933..1f24d229f2 100644 --- a/src/test/rpc/JSONRPC_test.cpp +++ b/src/test/rpc/JSONRPC_test.cpp @@ -2697,7 +2697,7 @@ public: testcase("autofill NetworkID"); using namespace test::jtx; Env env{*this, envconfig([&](std::unique_ptr cfg) { - cfg->NETWORK_ID = 1025; + cfg->networkId = 1025; return cfg; })}; @@ -2743,7 +2743,7 @@ public: // "c" (phantom signer) is rPcNzota6B8YBokhYtcTNqQVCngtbnWfux. Env env(*this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; return cfg; })); env.fund(XRP(100000), a, ed, g); diff --git a/src/test/rpc/KeyGeneration_test.cpp b/src/test/rpc/KeyGeneration_test.cpp index bb1f95bef1..aafe6f75a5 100644 --- a/src/test/rpc/KeyGeneration_test.cpp +++ b/src/test/rpc/KeyGeneration_test.cpp @@ -19,15 +19,15 @@ namespace xrpl::RPC { struct KeyStrings { - char const* account_id; - char const* master_key; - char const* master_seed; - char const* master_seed_hex; - char const* public_key; - char const* public_key_hex; - char const* secret_key_hex; + char const* accountId; + char const* masterKey; + char const* masterSeed; + char const* masterSeedHex; + char const* publicKey; + char const* publicKeyHex; + char const* secretKeyHex; char const* passphrase; - char const* passphrase_warning; + char const* passphraseWarning; }; namespace common { @@ -38,45 +38,45 @@ static char const* gMasterSeedHex = "BE6A670A19B209E112146D0A7ED2AAD7"; } // namespace common static KeyStrings const kSecP256K1Strings = { - .account_id = "r4Vtj2jrfmTVZGfSP3gH9hQPMqFPQFin8f", - .master_key = common::gMasterKey, - .master_seed = common::gMasterSeed, - .master_seed_hex = common::gMasterSeedHex, - .public_key = "aBQxK2YFNqzmAaXNczYcjqDjfiKkLsJUizsr1UBf44RCF8FHdrmX", - .public_key_hex = "038AAE247B2344B1837FBED8F57389C8C11774510A3F7D784F2A09F0CB6843236C", - .secret_key_hex = "1949ECD889EA71324BC7A30C8E81F4E93CB73EE19D59E9082111E78CC3DDABC2", + .accountId = "r4Vtj2jrfmTVZGfSP3gH9hQPMqFPQFin8f", + .masterKey = common::gMasterKey, + .masterSeed = common::gMasterSeed, + .masterSeedHex = common::gMasterSeedHex, + .publicKey = "aBQxK2YFNqzmAaXNczYcjqDjfiKkLsJUizsr1UBf44RCF8FHdrmX", + .publicKeyHex = "038AAE247B2344B1837FBED8F57389C8C11774510A3F7D784F2A09F0CB6843236C", + .secretKeyHex = "1949ECD889EA71324BC7A30C8E81F4E93CB73EE19D59E9082111E78CC3DDABC2", .passphrase = common::gPassphrase, - .passphrase_warning = + .passphraseWarning = "This wallet was generated using a user-supplied " "passphrase that has low entropy and is vulnerable " "to brute-force attacks.", }; static KeyStrings const kED25519Strings = { - .account_id = "r4qV6xTXerqaZav3MJfSY79ynmc1BSBev1", - .master_key = common::gMasterKey, - .master_seed = common::gMasterSeed, - .master_seed_hex = common::gMasterSeedHex, - .public_key = "aKEQmgLMyZPMruJFejUuedp169LgW6DbJt1rej1DJ5hWUMH4pHJ7", - .public_key_hex = "ED54C3F5BEDA8BD588B203D23A27398FAD9D20F88A974007D6994659CD7273FE1D", - .secret_key_hex = "77AAED2698D56D6676323629160F4EEF21CFD9EE3D0745CC78FA291461F98278", + .accountId = "r4qV6xTXerqaZav3MJfSY79ynmc1BSBev1", + .masterKey = common::gMasterKey, + .masterSeed = common::gMasterSeed, + .masterSeedHex = common::gMasterSeedHex, + .publicKey = "aKEQmgLMyZPMruJFejUuedp169LgW6DbJt1rej1DJ5hWUMH4pHJ7", + .publicKeyHex = "ED54C3F5BEDA8BD588B203D23A27398FAD9D20F88A974007D6994659CD7273FE1D", + .secretKeyHex = "77AAED2698D56D6676323629160F4EEF21CFD9EE3D0745CC78FA291461F98278", .passphrase = common::gPassphrase, - .passphrase_warning = + .passphraseWarning = "This wallet was generated using a user-supplied " "passphrase that has low entropy and is vulnerable " "to brute-force attacks.", }; static KeyStrings const kStrongBrainStrings = { - .account_id = "rBcvXmNb7KPkNdMkpckdWPpbvkWgcV3nir", - .master_key = "TED AVON CAVE HOUR BRAG JEFF RIFT NEAL TOLD FAT SEW SAN", - .master_seed = "shKdhWka8hS7Es3bpctCZXBiAwfUN", - .master_seed_hex = "74BA8389B44F98CF41E795CD91F9C93F", - .public_key = "aBRL2sqVuzrsM6zikPB4v8UBHGn1aKkrsxhYEffhcQxB2LKyywE5", - .public_key_hex = "03BD334FB9E06C58D69603E9922686528B18A754BC2F2E1ADA095FFE67DE952C64", - .secret_key_hex = "84262FB16AA25BE407174C7EDAB531220C30FA4D8A28AA9D564673FB3D34502C", + .accountId = "rBcvXmNb7KPkNdMkpckdWPpbvkWgcV3nir", + .masterKey = "TED AVON CAVE HOUR BRAG JEFF RIFT NEAL TOLD FAT SEW SAN", + .masterSeed = "shKdhWka8hS7Es3bpctCZXBiAwfUN", + .masterSeedHex = "74BA8389B44F98CF41E795CD91F9C93F", + .publicKey = "aBRL2sqVuzrsM6zikPB4v8UBHGn1aKkrsxhYEffhcQxB2LKyywE5", + .publicKeyHex = "03BD334FB9E06C58D69603E9922686528B18A754BC2F2E1ADA095FFE67DE952C64", + .secretKeyHex = "84262FB16AA25BE407174C7EDAB531220C30FA4D8A28AA9D564673FB3D34502C", .passphrase = "A4yKIRGdzrw0YQ$2%TFKYG9HP*&ok^!sy7E@RwICs", - .passphrase_warning = + .passphraseWarning = "This wallet was generated using a user-supplied " "passphrase. It may be vulnerable to brute-force " "attacks.", @@ -120,11 +120,11 @@ public: json::Value result = walletPropose(params); BEAST_EXPECT(!containsError(result)); - expectEquals(result[jss::account_id], s.account_id); - expectEquals(result[jss::master_seed], s.master_seed); - expectEquals(result[jss::master_seed_hex], s.master_seed_hex); - expectEquals(result[jss::public_key], s.public_key); - expectEquals(result[jss::public_key_hex], s.public_key_hex); + expectEquals(result[jss::account_id], s.accountId); + expectEquals(result[jss::master_seed], s.masterSeed); + expectEquals(result[jss::master_seed_hex], s.masterSeedHex); + expectEquals(result[jss::public_key], s.publicKey); + expectEquals(result[jss::public_key_hex], s.publicKeyHex); expectEquals( result[jss::key_type], params.isMember(jss::key_type) ? params[jss::key_type] : "secp256k1"); @@ -139,7 +139,7 @@ public: json::Value params; if (keyType) params[jss::key_type] = *keyType; - params[jss::seed] = strings.master_seed; + params[jss::seed] = strings.masterSeed; auto const wallet = testSecretWallet(params, strings); BEAST_EXPECT(!wallet.isMember(jss::warning)); @@ -153,7 +153,7 @@ public: json::Value params; if (keyType) params[jss::key_type] = *keyType; - params[jss::seed_hex] = strings.master_seed_hex; + params[jss::seed_hex] = strings.masterSeedHex; auto const wallet = testSecretWallet(params, strings); BEAST_EXPECT(!wallet.isMember(jss::warning)); @@ -173,7 +173,7 @@ public: auto const wallet = testSecretWallet(params, strings); if (value == strings.passphrase) { - BEAST_EXPECT(wallet[jss::warning] == strings.passphrase_warning); + BEAST_EXPECT(wallet[jss::warning] == strings.passphraseWarning); } else { @@ -187,9 +187,9 @@ public: testcase("passphrase"); testLegacyPassphrase(strings.passphrase, keyType, strings); - testLegacyPassphrase(strings.master_key, keyType, strings); - testLegacyPassphrase(strings.master_seed, keyType, strings); - testLegacyPassphrase(strings.master_seed_hex, keyType, strings); + testLegacyPassphrase(strings.masterKey, keyType, strings); + testLegacyPassphrase(strings.masterSeed, keyType, strings); + testLegacyPassphrase(strings.masterSeedHex, keyType, strings); } void @@ -205,8 +205,8 @@ public: json::Value params; if (keyType) params[jss::key_type] = *keyType; - params[jss::seed] = strings.master_seed; - params[jss::seed_hex] = strings.master_seed_hex; + params[jss::seed] = strings.masterSeed; + params[jss::seed_hex] = strings.masterSeedHex; // Secret fields are mutually exclusive. BEAST_EXPECT(containsError(walletPropose(params))); @@ -294,7 +294,7 @@ public: { testcase("keypairForSignature - " + (keyType ? *keyType : "no key_type")); - auto const publicKey = parseBase58(TokenType::AccountPublic, strings.public_key); + auto const publicKey = parseBase58(TokenType::AccountPublic, strings.publicKey); BEAST_EXPECT(publicKey); if (!keyType) @@ -302,7 +302,7 @@ public: { json::Value params; json::Value error; - params[jss::secret] = strings.master_seed; + params[jss::secret] = strings.masterSeed; auto ret = keypairForSignature(params, error); BEAST_EXPECT(!containsError(error)); @@ -316,7 +316,7 @@ public: { json::Value params; json::Value error; - params[jss::secret] = strings.master_seed_hex; + params[jss::secret] = strings.masterSeedHex; auto ret = keypairForSignature(params, error); BEAST_EXPECT(!containsError(error)); @@ -330,7 +330,7 @@ public: { json::Value params; json::Value error; - params[jss::secret] = strings.master_key; + params[jss::secret] = strings.masterKey; auto ret = keypairForSignature(params, error); BEAST_EXPECT(!containsError(error)); @@ -349,7 +349,7 @@ public: json::Value error; params[jss::key_type] = *keyType; - params[jss::seed] = strings.master_seed; + params[jss::seed] = strings.masterSeed; auto ret = keypairForSignature(params, error); BEAST_EXPECT(!containsError(error)); @@ -365,7 +365,7 @@ public: json::Value error; params[jss::key_type] = *keyType; - params[jss::seed_hex] = strings.master_seed_hex; + params[jss::seed_hex] = strings.masterSeedHex; auto ret = keypairForSignature(params, error); BEAST_EXPECT(!containsError(error)); @@ -381,7 +381,7 @@ public: json::Value error; params[jss::key_type] = *keyType; - params[jss::passphrase] = strings.master_key; + params[jss::passphrase] = strings.masterKey; auto ret = keypairForSignature(params, error); BEAST_EXPECT(!containsError(error)); diff --git a/src/test/rpc/LedgerClosed_test.cpp b/src/test/rpc/LedgerClosed_test.cpp index 81919e04d0..f02d23030d 100644 --- a/src/test/rpc/LedgerClosed_test.cpp +++ b/src/test/rpc/LedgerClosed_test.cpp @@ -23,7 +23,7 @@ public: // This test relies on ledger hash so must lock it to fee 10. auto p = envconfig(); - p->FEES.reference_fee = 10; + p->fees.referenceFee = 10; Env env{*this, std::move(p), FeatureBitset{}}; Account const alice{"alice"}; env.fund(XRP(10000), alice); diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index 0f9421beb2..d231a2d4a0 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -555,7 +555,7 @@ class LedgerEntry_test : public beast::unit_test::Suite using namespace test::jtx; auto cfg = envconfig(); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env{*this, std::move(cfg)}; Account const alice{"alice"}; @@ -2237,7 +2237,7 @@ class LedgerEntry_test : public beast::unit_test::Suite Account const bob{"bob"}; Env env{*this, envconfig([](auto cfg) { - cfg->START_UP = StartUpType::Fresh; + cfg->startUp = StartUpType::Fresh; return cfg; })}; @@ -2430,7 +2430,7 @@ class LedgerEntry_test : public beast::unit_test::Suite Account const bob{"bob"}; Env env{*this, envconfig([](auto cfg) { - cfg->START_UP = StartUpType::Fresh; + cfg->startUp = StartUpType::Fresh; return cfg; })}; diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index f8d7901ec0..f35dddffb1 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -268,7 +268,7 @@ class LedgerRPC_test : public beast::unit_test::Suite using namespace test::jtx; auto cfg = envconfig(); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env{*this, std::move(cfg), FeatureBitset{}}; // hashes requested below // assume no amendments env.fund(XRP(10000), "alice"); @@ -437,7 +437,7 @@ class LedgerRPC_test : public beast::unit_test::Suite return cfg; }); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env(*this, std::move(cfg)); json::Value jv; diff --git a/src/test/rpc/LedgerRequest_test.cpp b/src/test/rpc/LedgerRequest_test.cpp index 0a2b51dc8b..4759256b96 100644 --- a/src/test/rpc/LedgerRequest_test.cpp +++ b/src/test/rpc/LedgerRequest_test.cpp @@ -149,7 +149,7 @@ public: using namespace test::jtx; auto cfg = envconfig(); - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; Env env{*this, std::move(cfg), FeatureBitset{}}; // the hashes being checked below // assume no amendments Account const gw{"gateway"}; @@ -301,8 +301,8 @@ public: using namespace test::jtx; using namespace std::chrono_literals; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = 10; - cfg->NODE_SIZE = 0; + cfg->fees.referenceFee = 10; + cfg->nodeSize = 0; return cfg; })}; Account const gw{"gateway"}; diff --git a/src/test/rpc/NoRippleCheck_test.cpp b/src/test/rpc/NoRippleCheck_test.cpp index 3793efffcd..9c17d291a1 100644 --- a/src/test/rpc/NoRippleCheck_test.cpp +++ b/src/test/rpc/NoRippleCheck_test.cpp @@ -296,7 +296,7 @@ class NoRippleCheckLimits_test : public beast::unit_test::Suite if (c.balance() > kWarningThreshold) { using ct = beast::AbstractClock; - c.entry().local_balance = + c.entry().localBalance = DecayingSample{steady_clock::now()}; } }; diff --git a/src/test/rpc/RPCCall_test.cpp b/src/test/rpc/RPCCall_test.cpp index 66ddfde8e2..cd3cd3cb41 100644 --- a/src/test/rpc/RPCCall_test.cpp +++ b/src/test/rpc/RPCCall_test.cpp @@ -5843,7 +5843,7 @@ makeNetworkConfig(uint32_t networkID) { using namespace test::jtx; return envconfig([&](std::unique_ptr cfg) { - cfg->NETWORK_ID = networkID; + cfg->networkId = networkID; return cfg; }); } diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 60452e386a..9206bf42ac 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -492,7 +492,7 @@ class Simulate_test : public beast::unit_test::Suite using namespace jtx; Env env{*this, envconfig([&](std::unique_ptr cfg) { - cfg->NETWORK_ID = 0; + cfg->networkId = 0; return cfg; })}; static auto const kNewDomain = "123ABC"; @@ -1032,7 +1032,7 @@ class Simulate_test : public beast::unit_test::Suite using namespace jtx; Env env{*this, envconfig([&](std::unique_ptr cfg) { - cfg->NETWORK_ID = 1025; + cfg->networkId = 1025; return cfg; })}; static auto const kNewDomain = "123ABC"; @@ -1097,7 +1097,7 @@ class Simulate_test : public beast::unit_test::Suite using namespace jtx; using namespace std::chrono_literals; Env env{*this, envconfig([&](std::unique_ptr cfg) { - cfg->NETWORK_ID = 1025; + cfg->networkId = 1025; return cfg; })}; diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index 3011eb4170..090a599e7c 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -321,7 +321,7 @@ public: using namespace std::chrono_literals; using namespace jtx; Env env(*this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; cfg = singleThreadIo(std::move(cfg)); return cfg; })); @@ -1319,7 +1319,7 @@ public: auto const carol = permDex.carol; auto const domainID = permDex.domainID; auto const gw = permDex.gw; - auto const usd = permDex.USD; + auto const usd = permDex.usd; auto wsc = makeWSClient(env.app().config()); diff --git a/src/test/rpc/TransactionEntry_test.cpp b/src/test/rpc/TransactionEntry_test.cpp index c5f562b2b2..8724e2974d 100644 --- a/src/test/rpc/TransactionEntry_test.cpp +++ b/src/test/rpc/TransactionEntry_test.cpp @@ -32,7 +32,7 @@ class TransactionEntry_test : public beast::unit_test::Suite testcase("Invalid request params"); using namespace test::jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; return cfg; })}; @@ -139,7 +139,7 @@ class TransactionEntry_test : public beast::unit_test::Suite testcase("Basic request API version " + std::to_string(apiVersion)); using namespace test::jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; return cfg; })}; diff --git a/src/test/rpc/Transaction_test.cpp b/src/test/rpc/Transaction_test.cpp index 47cc3687b0..8c50736b85 100644 --- a/src/test/rpc/Transaction_test.cpp +++ b/src/test/rpc/Transaction_test.cpp @@ -47,7 +47,7 @@ class Transaction_test : public beast::unit_test::Suite { using namespace test::jtx; return envconfig([&](std::unique_ptr cfg) { - cfg->NETWORK_ID = networkID; + cfg->networkId = networkID; return cfg; }); } @@ -749,7 +749,7 @@ class Transaction_test : public beast::unit_test::Suite using std::to_string; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; return cfg; })}; Account const alice{"alice"}; @@ -827,7 +827,7 @@ class Transaction_test : public beast::unit_test::Suite using std::to_string; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->FEES.reference_fee = 10; + cfg->fees.referenceFee = 10; return cfg; })}; Account const alice{"alice"}; diff --git a/src/test/rpc/Version_test.cpp b/src/test/rpc/Version_test.cpp index 75d74b4155..20740bfe53 100644 --- a/src/test/rpc/Version_test.cpp +++ b/src/test/rpc/Version_test.cpp @@ -65,7 +65,7 @@ class Version_test : public beast::unit_test::Suite "{\"api_version\": " + std::to_string(RPC::kApiMinimumSupportedVersion - 1) + "}"); BEAST_EXPECT(badVersion(re)); - BEAST_EXPECT(env.app().config().BETA_RPC_API); + BEAST_EXPECT(env.app().config().betaRpcApi); re = env.rpc( "json", "version", @@ -160,7 +160,7 @@ class Version_test : public beast::unit_test::Suite using namespace test::jtx; Env env{*this}; - BEAST_EXPECT(env.app().config().BETA_RPC_API); + BEAST_EXPECT(env.app().config().betaRpcApi); auto const withoutApiVerion = std::string("{ ") + "\"jsonrpc\": \"2.0\", " "\"ripplerpc\": \"2.0\", " @@ -194,19 +194,19 @@ class Version_test : public beast::unit_test::Suite testcase("config test"); { Config const c; - BEAST_EXPECT(c.BETA_RPC_API == false); + BEAST_EXPECT(c.betaRpcApi == false); } { Config c; c.loadFromString("\n[beta_rpc_api]\n1\n"); - BEAST_EXPECT(c.BETA_RPC_API == true); + BEAST_EXPECT(c.betaRpcApi == true); } { Config c; c.loadFromString("\n[beta_rpc_api]\n0\n"); - BEAST_EXPECT(c.BETA_RPC_API == false); + BEAST_EXPECT(c.betaRpcApi == false); } } @@ -220,7 +220,7 @@ class Version_test : public beast::unit_test::Suite c->loadFromString("\n[beta_rpc_api]\n1\n"); return c; })}; - if (!BEAST_EXPECT(env.app().config().BETA_RPC_API == true)) + if (!BEAST_EXPECT(env.app().config().betaRpcApi == true)) return; auto jrr = env.rpc( diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index eba36462b6..6569a2d2a4 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -843,7 +843,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En BEAST_EXPECT(resp.body().find("connectivity is working.") != std::string::npos); // with ELB_SUPPORT, status still does not indicate a problem - env.app().config().ELB_SUPPORT = true; + env.app().config().elbSupport = true; doRequest( yield, @@ -973,7 +973,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En BEAST_EXPECT(resp.result() == boost::beast::http::status::ok); BEAST_EXPECT(resp.body().find("connectivity is working.") != std::string::npos); - env.app().config().ELB_SUPPORT = true; + env.app().config().elbSupport = true; doRequest( yield, @@ -1111,7 +1111,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En using namespace test::jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->ELB_SUPPORT = true; + cfg->elbSupport = true; return cfg; })}; diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp index 1dc40f6337..263fb9451f 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -48,15 +48,15 @@ public: class TestThread { private: - boost::asio::io_context io_context_; + boost::asio::io_context ioContext_; std::optional> work_; std::thread thread_; public: TestThread() - : work_(std::in_place, boost::asio::make_work_guard(io_context_)) - , thread_([&]() { this->io_context_.run(); }) + : work_(std::in_place, boost::asio::make_work_guard(ioContext_)) + , thread_([&]() { this->ioContext_.run(); }) { } @@ -69,7 +69,7 @@ public: boost::asio::io_context& getIoContext() { - return io_context_; + return ioContext_; } }; diff --git a/src/test/unit_test/multi_runner.cpp b/src/test/unit_test/multi_runner.cpp index 60e979299d..3a56d22654 100644 --- a/src/test/unit_test/multi_runner.cpp +++ b/src/test/unit_test/multi_runner.cpp @@ -139,28 +139,28 @@ template std::size_t MultiRunnerBase::Inner::checkoutJobIndex() { - return job_index++; + return jobIndex++; } template std::size_t MultiRunnerBase::Inner::checkoutTestIndex() { - return test_index++; + return testIndex++; } template bool MultiRunnerBase::Inner::anyFailed() const { - return any_failed; + return anyFailedFlag; } template void MultiRunnerBase::Inner::anyFailed(bool v) { - any_failed = any_failed || v; + anyFailedFlag = anyFailedFlag || v; } template @@ -183,14 +183,14 @@ template void MultiRunnerBase::Inner::incKeepAliveCount() { - ++keep_alive; + ++keepAlive; } template std::size_t MultiRunnerBase::Inner::getKeepAliveCount() { - return keep_alive; + return keepAlive; } template @@ -222,7 +222,7 @@ MultiRunnerBase::MultiRunnerBase() boost::interprocess::message_queue::remove(kMessageQueueName); } - shared_mem_ = boost::interprocess::shared_memory_object{ + sharedMem_ = boost::interprocess::shared_memory_object{ std::conditional_t< IsParent, boost::interprocess::create_only_t, @@ -232,8 +232,8 @@ MultiRunnerBase::MultiRunnerBase() if (IsParent) { - shared_mem_.truncate(sizeof(Inner)); - message_queue_ = std::make_unique( + sharedMem_.truncate(sizeof(Inner)); + messageQueue_ = std::make_unique( boost::interprocess::create_only, kMessageQueueName, /*max messages*/ 16, @@ -241,11 +241,11 @@ MultiRunnerBase::MultiRunnerBase() } else { - message_queue_ = std::make_unique( + messageQueue_ = std::make_unique( boost::interprocess::open_only, kMessageQueueName); } - region_ = boost::interprocess::mapped_region{shared_mem_, boost::interprocess::read_write}; + region_ = boost::interprocess::mapped_region{sharedMem_, boost::interprocess::read_write}; if (IsParent) { inner_ = new (region_.get_address()) Inner{}; @@ -340,8 +340,8 @@ MultiRunnerBase::messageQueueSend(MessageType mt, std::string const& s { // must use a mutex since the two "sends" must happen in order std::scoped_lock const l{inner_->m}; - message_queue_->send(&mt, sizeof(mt), /*priority*/ 0); - message_queue_->send(s.c_str(), s.size(), /*priority*/ 0); + messageQueue_->send(&mt, sizeof(mt), /*priority*/ 0); + messageQueue_->send(s.c_str(), s.size(), /*priority*/ 0); } template @@ -376,13 +376,13 @@ namespace test { MultiRunnerParent::MultiRunnerParent() : os_(std::cout) { - message_queue_thread_ = std::thread([this] { + messageQueueThread_ = std::thread([this] { std::vector buf(1 << 20); - while (this->continue_message_queue_ || this->message_queue_->get_num_msg()) + while (this->continueMessageQueue_ || this->messageQueue_->get_num_msg()) { // let children know the parent is still alive this->incKeepAliveCount(); - if (!this->message_queue_->get_num_msg()) + if (!this->messageQueue_->get_num_msg()) { // If a child does not see the keep alive count incremented, // it will assume the parent has died. This sleep time needs @@ -395,13 +395,13 @@ MultiRunnerParent::MultiRunnerParent() : os_(std::cout) { std::size_t recvdSize = 0; unsigned int priority = 0; - this->message_queue_->receive(buf.data(), buf.size(), recvdSize, priority); + this->messageQueue_->receive(buf.data(), buf.size(), recvdSize, priority); if (!recvdSize) continue; assert(recvdSize == 1); MessageType const mt{*reinterpret_cast(buf.data())}; - this->message_queue_->receive(buf.data(), buf.size(), recvdSize, priority); + this->messageQueue_->receive(buf.data(), buf.size(), recvdSize, priority); if (recvdSize) { std::string s{buf.data(), recvdSize}; @@ -412,10 +412,10 @@ MultiRunnerParent::MultiRunnerParent() : os_(std::cout) this->os_.flush(); break; case MessageType::TestStart: - running_suites_.insert(std::move(s)); + runningSuites_.insert(std::move(s)); break; case MessageType::TestEnd: - running_suites_.erase(s); + runningSuites_.erase(s); break; default: assert(0); // unknown message type @@ -440,14 +440,14 @@ MultiRunnerParent::~MultiRunnerParent() { using namespace beast::unit_test; - continue_message_queue_ = false; - message_queue_thread_.join(); + continueMessageQueue_ = false; + messageQueueThread_.join(); - addFailures(running_suites_.size()); + addFailures(runningSuites_.size()); printResults(os_); - for (auto const& s : running_suites_) + for (auto const& s : runningSuites_) { os_ << "\nSuite: " << s << " failed to complete. The child process may have crashed.\n"; } @@ -480,16 +480,13 @@ MultiRunnerParent::addFailures(std::size_t failures) //------------------------------------------------------------------------------ MultiRunnerChild::MultiRunnerChild(std::size_t numJobs, bool quiet, bool printLog) - : job_index_{checkoutJobIndex()} - , num_jobs_{numJobs} - , quiet_{quiet} - , print_log_{!quiet || printLog} + : jobIndex_{checkoutJobIndex()}, numJobs_{numJobs}, quiet_{quiet}, printLog_{!quiet || printLog} { - if (num_jobs_ > 1) + if (numJobs_ > 1) { - keep_alive_thread_ = std::thread([this] { + keepAliveThread_ = std::thread([this] { std::size_t lastCount = getKeepAliveCount(); - while (this->continue_keep_alive_) + while (this->continueKeepAlive_) { // Use a small sleep time so in the normal case the child // process may shutdown quickly. However, to protect against @@ -504,7 +501,7 @@ MultiRunnerChild::MultiRunnerChild(std::size_t numJobs, bool quiet, bool printLo if (curCount == lastCount) { // assume parent process is no longer alive - std::cerr << "multi_runner_child " << job_index_ + std::cerr << "multi_runner_child " << jobIndex_ << ": Assuming parent died, exiting.\n"; std::exit(EXIT_FAILURE); } @@ -517,10 +514,10 @@ MultiRunnerChild::MultiRunnerChild(std::size_t numJobs, bool quiet, bool printLo MultiRunnerChild::~MultiRunnerChild() { - if (num_jobs_ > 1) + if (numJobs_ > 1) { - continue_keep_alive_ = false; - keep_alive_thread_.join(); + continueKeepAlive_ = false; + keepAliveThread_.join(); } add(results_); @@ -548,75 +545,74 @@ MultiRunnerChild::addFailures(std::size_t failures) void MultiRunnerChild::onSuiteBegin(beast::unit_test::SuiteInfo const& info) { - suite_results_ = detail::SuiteResults{info.fullName()}; - messageQueueSend(MessageType::TestStart, suite_results_.name); + suiteResults_ = detail::SuiteResults{info.fullName()}; + messageQueueSend(MessageType::TestStart, suiteResults_.name); } void MultiRunnerChild::onSuiteEnd() { - if (print_log_ || suite_results_.failed > 0) + if (printLog_ || suiteResults_.failed > 0) { std::stringstream s; - if (num_jobs_ > 1) - s << job_index_ << "> "; - s << (suite_results_.failed > 0 ? "failed: " : "") << suite_results_.name << " had " - << suite_results_.failed << " failures." << std::endl; + if (numJobs_ > 1) + s << jobIndex_ << "> "; + s << (suiteResults_.failed > 0 ? "failed: " : "") << suiteResults_.name << " had " + << suiteResults_.failed << " failures." << std::endl; messageQueueSend(MessageType::Log, s.str()); } - results_.add(suite_results_); - messageQueueSend(MessageType::TestEnd, suite_results_.name); + results_.add(suiteResults_); + messageQueueSend(MessageType::TestEnd, suiteResults_.name); } void MultiRunnerChild::onCaseBegin(std::string const& name) { - case_results_ = detail::CaseResults(name); + caseResults_ = detail::CaseResults(name); if (quiet_) return; std::stringstream s; - if (num_jobs_ > 1) - s << job_index_ << "> "; - s << suite_results_.name << (case_results_.name.empty() ? "" : (" " + case_results_.name)) - << '\n'; + if (numJobs_ > 1) + s << jobIndex_ << "> "; + s << suiteResults_.name << (caseResults_.name.empty() ? "" : (" " + caseResults_.name)) << '\n'; messageQueueSend(MessageType::Log, s.str()); } void MultiRunnerChild::onCaseEnd() { - suite_results_.add(case_results_); + suiteResults_.add(caseResults_); } void MultiRunnerChild::onPass() { - ++case_results_.total; + ++caseResults_.total; } void MultiRunnerChild::onFail(std::string const& reason) { - ++case_results_.failed; - ++case_results_.total; + ++caseResults_.failed; + ++caseResults_.total; std::stringstream s; - if (num_jobs_ > 1) - s << job_index_ << "> "; - s << "#" << case_results_.total << " failed" << (reason.empty() ? "" : ": ") << reason << '\n'; + if (numJobs_ > 1) + s << jobIndex_ << "> "; + s << "#" << caseResults_.total << " failed" << (reason.empty() ? "" : ": ") << reason << '\n'; messageQueueSend(MessageType::Log, s.str()); } void MultiRunnerChild::onLog(std::string const& msg) { - if (!print_log_) + if (!printLog_) return; std::stringstream s; - if (num_jobs_ > 1) - s << job_index_ << "> "; + if (numJobs_ > 1) + s << jobIndex_ << "> "; s << msg; messageQueueSend(MessageType::Log, s.str()); } diff --git a/src/test/unit_test/multi_runner.h b/src/test/unit_test/multi_runner.h index 5ebc8ec0a8..8b07559e8c 100644 --- a/src/test/unit_test/multi_runner.h +++ b/src/test/unit_test/multi_runner.h @@ -87,14 +87,14 @@ class MultiRunnerBase // way they communicate is through message queues. struct Inner { - std::atomic job_index{0}; - std::atomic test_index{0}; - std::atomic any_failed{false}; - // A parent process will periodically increment `keep_alive_`. The child - // processes will check if `keep_alive_` is being incremented. If it is + std::atomic jobIndex{0}; + std::atomic testIndex{0}; + std::atomic anyFailedFlag{false}; + // A parent process will periodically increment `keepAlive`. The child + // processes will check if `keepAlive` is being incremented. If it is // not incremented for a sufficiently long time, the child will assume // the parent process has died. - std::atomic keep_alive{0}; + std::atomic keepAlive{0}; mutable boost::interprocess::interprocess_mutex m; detail::Results results; @@ -139,11 +139,11 @@ class MultiRunnerBase // `inner_` will be created in shared memory Inner* inner_; // shared memory to use for the `inner` member - boost::interprocess::shared_memory_object shared_mem_; + boost::interprocess::shared_memory_object sharedMem_; boost::interprocess::mapped_region region_; protected: - std::unique_ptr message_queue_; + std::unique_ptr messageQueue_; enum class MessageType : std::uint8_t { TestStart, TestEnd, Log }; void @@ -201,10 +201,10 @@ class MultiRunnerParent : private detail::MultiRunnerBase private: // message_queue_ is used to collect log messages from the children std::ostream& os_; - std::atomic continue_message_queue_{true}; - std::thread message_queue_thread_; + std::atomic continueMessageQueue_{true}; + std::thread messageQueueThread_; // track running suites so if a child crashes the culprit can be flagged - std::set running_suites_; + std::set runningSuites_; public: MultiRunnerParent(MultiRunnerParent const&) = delete; @@ -235,16 +235,16 @@ class MultiRunnerChild : public beast::unit_test::Runner, private detail::MultiRunnerBase { private: - std::size_t job_index_; + std::size_t jobIndex_; detail::Results results_; - detail::SuiteResults suite_results_; - detail::CaseResults case_results_; - std::size_t num_jobs_{0}; + detail::SuiteResults suiteResults_; + detail::CaseResults caseResults_; + std::size_t numJobs_{0}; bool quiet_{false}; - bool print_log_{true}; + bool printLog_{true}; - std::atomic continue_keep_alive_{true}; - std::thread keep_alive_thread_; + std::atomic continueKeepAlive_{true}; + std::thread keepAliveThread_; public: MultiRunnerChild(MultiRunnerChild const&) = delete; @@ -318,12 +318,12 @@ MultiRunnerChild::runMulti(Pred pred) } catch (...) { - if (num_jobs_ <= 1) + if (numJobs_ <= 1) throw; // a single process can die // inform the parent std::stringstream s; - s << job_index_ << "> failed Unhandled exception in test.\n"; + s << jobIndex_ << "> failed Unhandled exception in test.\n"; messageQueueSend(MessageType::Log, s.str()); failed = true; } diff --git a/src/xrpld/app/ledger/LedgerHistory.cpp b/src/xrpld/app/ledger/LedgerHistory.cpp index f251f25620..8520fc941f 100644 --- a/src/xrpld/app/ledger/LedgerHistory.cpp +++ b/src/xrpld/app/ledger/LedgerHistory.cpp @@ -36,14 +36,14 @@ namespace xrpl { LedgerHistory::LedgerHistory(beast::insight::Collector::ptr const& collector, Application& app) : app_(app) , collector_(collector) - , mismatch_counter_(collector->makeCounter("ledger.history", "mismatch")) - , ledgers_by_hash_( + , mismatchCounter_(collector->makeCounter("ledger.history", "mismatch")) + , ledgersByHash_( "LedgerCache", app_.config().getValueFor(SizedItem::LedgerSize), std::chrono::seconds{app_.config().getValueFor(SizedItem::LedgerAge)}, stopwatch(), app_.getJournal("TaggedCache")) - , consensus_validated_( + , consensusValidated_( "ConsensusValidated", 64, std::chrono::minutes{5}, @@ -62,10 +62,9 @@ LedgerHistory::insert(std::shared_ptr const& ledger, bool validate XRPL_ASSERT( ledger->stateMap().getHash().isNonZero(), "xrpl::LedgerHistory::insert : nonzero hash"); - std::unique_lock const sl(ledgers_by_hash_.peekMutex()); + std::unique_lock const sl(ledgersByHash_.peekMutex()); - bool const alreadyHad = - ledgers_by_hash_.canonicalizeReplaceCache(ledger->header().hash, ledger); + bool const alreadyHad = ledgersByHash_.canonicalizeReplaceCache(ledger->header().hash, ledger); if (validated) ledgersByIndex_[ledger->header().seq] = ledger->header().hash; @@ -75,7 +74,7 @@ LedgerHistory::insert(std::shared_ptr const& ledger, bool validate LedgerHash LedgerHistory::getLedgerHash(LedgerIndex index) { - std::unique_lock const sl(ledgers_by_hash_.peekMutex()); + std::unique_lock const sl(ledgersByHash_.peekMutex()); if (auto it = ledgersByIndex_.find(index); it != ledgersByIndex_.end()) return it->second; return {}; @@ -85,7 +84,7 @@ std::shared_ptr LedgerHistory::getLedgerBySeq(LedgerIndex index) { { - std::unique_lock sl(ledgers_by_hash_.peekMutex()); + std::unique_lock sl(ledgersByHash_.peekMutex()); auto it = ledgersByIndex_.find(index); if (it != ledgersByIndex_.end()) @@ -97,7 +96,7 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index) } Rules const rules{app_.config().features}; - Fees const fees = app_.config().FEES.toFees(); + Fees const fees = app_.config().fees.toFees(); std::shared_ptr ret = loadByIndex(index, rules, fees, app_); if (!ret) @@ -108,11 +107,11 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index) { // Add this ledger to the local tracking by index - std::unique_lock const sl(ledgers_by_hash_.peekMutex()); + std::unique_lock const sl(ledgersByHash_.peekMutex()); XRPL_ASSERT( ret->isImmutable(), "xrpl::LedgerHistory::getLedgerBySeq : immutable result ledger"); - ledgers_by_hash_.canonicalizeReplaceClient(ret->header().hash, ret); + ledgersByHash_.canonicalizeReplaceClient(ret->header().hash, ret); ledgersByIndex_[ret->header().seq] = ret->header().hash; return (ret->header().seq == index) ? ret : nullptr; } @@ -121,7 +120,7 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index) std::shared_ptr LedgerHistory::getLedgerByHash(LedgerHash const& hash) { - auto ret = ledgers_by_hash_.fetch(hash); + auto ret = ledgersByHash_.fetch(hash); if (ret) { @@ -137,7 +136,7 @@ LedgerHistory::getLedgerByHash(LedgerHash const& hash) } Rules const rules{app_.config().features}; - Fees const fees = app_.config().FEES.toFees(); + Fees const fees = app_.config().fees.toFees(); ret = loadByHash(hash, rules, fees, app_); if (!ret) @@ -148,7 +147,7 @@ LedgerHistory::getLedgerByHash(LedgerHash const& hash) XRPL_ASSERT( ret->header().hash == hash, "xrpl::LedgerHistory::getLedgerByHash : loaded ledger hash match"); - ledgers_by_hash_.canonicalizeReplaceClient(ret->header().hash, ret); + ledgersByHash_.canonicalizeReplaceClient(ret->header().hash, ret); XRPL_ASSERT( ret->header().hash == hash, "xrpl::LedgerHistory::getLedgerByHash : result hash match"); @@ -319,7 +318,7 @@ LedgerHistory::handleMismatch( json::Value const& consensus) { XRPL_ASSERT(built != valid, "xrpl::LedgerHistory::handleMismatch : unequal hashes"); - ++mismatch_counter_; + ++mismatchCounter_; auto builtLedger = getLedgerByHash(built); auto validLedger = getLedgerByHash(valid); @@ -430,10 +429,10 @@ LedgerHistory::builtLedger( LedgerHash const hash = ledger->header().hash; XRPL_ASSERT(!hash.isZero(), "xrpl::LedgerHistory::builtLedger : nonzero hash"); - std::unique_lock const sl(consensus_validated_.peekMutex()); + std::unique_lock const sl(consensusValidated_.peekMutex()); auto entry = std::make_shared(); - consensus_validated_.canonicalizeReplaceClient(index, entry); + consensusValidated_.canonicalizeReplaceClient(index, entry); if (entry->validated && !entry->built) { @@ -469,10 +468,10 @@ LedgerHistory::validatedLedger( LedgerHash const hash = ledger->header().hash; XRPL_ASSERT(!hash.isZero(), "xrpl::LedgerHistory::validatedLedger : nonzero hash"); - std::unique_lock const sl(consensus_validated_.peekMutex()); + std::unique_lock const sl(consensusValidated_.peekMutex()); auto entry = std::make_shared(); - consensus_validated_.canonicalizeReplaceClient(index, entry); + consensusValidated_.canonicalizeReplaceClient(index, entry); if (entry->built && !entry->validated) { @@ -504,7 +503,7 @@ LedgerHistory::validatedLedger( bool LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash) { - std::unique_lock const sl(ledgers_by_hash_.peekMutex()); + std::unique_lock const sl(ledgersByHash_.peekMutex()); auto it = ledgersByIndex_.find(ledgerIndex); if ((it != ledgersByIndex_.end()) && (it->second != ledgerHash)) @@ -518,11 +517,11 @@ LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash) void LedgerHistory::clearLedgerCachePrior(LedgerIndex seq) { - for (LedgerHash const it : ledgers_by_hash_.getKeys()) + for (LedgerHash const it : ledgersByHash_.getKeys()) { auto const ledger = getLedgerByHash(it); if (!ledger || ledger->header().seq < seq) - ledgers_by_hash_.del(it, false); + ledgersByHash_.del(it, false); } } diff --git a/src/xrpld/app/ledger/LedgerHistory.h b/src/xrpld/app/ledger/LedgerHistory.h index 01c88b1517..057de7b1bc 100644 --- a/src/xrpld/app/ledger/LedgerHistory.h +++ b/src/xrpld/app/ledger/LedgerHistory.h @@ -30,7 +30,7 @@ public: float getCacheHitRate() { - return ledgers_by_hash_.getHitRate(); + return ledgersByHash_.getHitRate(); } /** Get a ledger given its sequence number */ @@ -53,8 +53,8 @@ public: void sweep() { - ledgers_by_hash_.sweep(); - consensus_validated_.sweep(); + ledgersByHash_.sweep(); + consensusValidated_.sweep(); } /** Report that we have locally built a particular ledger */ @@ -99,11 +99,11 @@ private: Application& app_; beast::insight::Collector::ptr collector_; - beast::insight::Counter mismatch_counter_; + beast::insight::Counter mismatchCounter_; using LedgersByHash = TaggedCache; - LedgersByHash ledgers_by_hash_; + LedgersByHash ledgersByHash_; // Maps ledger indexes to the corresponding hashes // For debug and logging purposes @@ -121,7 +121,7 @@ private: std::optional consensus; }; using ConsensusValidated = TaggedCache; - ConsensusValidated consensus_validated_; + ConsensusValidated consensusValidated_; // Maps ledger indexes to the corresponding hash. std::map ledgersByIndex_; // validated ledgers diff --git a/src/xrpld/app/ledger/LedgerMaster.h b/src/xrpld/app/ledger/LedgerMaster.h index 0b3310ea79..885ab6db25 100644 --- a/src/xrpld/app/ledger/LedgerMaster.h +++ b/src/xrpld/app/ledger/LedgerMaster.h @@ -347,20 +347,20 @@ private: bool const standalone_; // How many ledgers before the current ledger do we allow peers to request? - std::uint32_t const fetch_depth_; + std::uint32_t const fetchDepth_; // How much history do we want to keep - std::uint32_t const ledger_history_; + std::uint32_t const ledgerHistorySize_; - std::uint32_t const ledger_fetch_size_; + std::uint32_t const ledgerFetchSize_; - TaggedCache fetch_packs_; + TaggedCache fetchPacks_; - std::uint32_t fetch_seq_{0}; + std::uint32_t fetchSeq_{0}; // Try to keep a validator from switching from test to live network // without first wiping the database. - LedgerIndex const max_ledger_difference_{1000000}; + LedgerIndex const maxLedgerDifference_{1000000}; // Time that the previous upgrade warning was issued. TimeKeeper::time_point upgradeWarningPrevTime_; diff --git a/src/xrpld/app/ledger/OpenLedger.h b/src/xrpld/app/ledger/OpenLedger.h index 884b6b02db..02e073bc9a 100644 --- a/src/xrpld/app/ledger/OpenLedger.h +++ b/src/xrpld/app/ledger/OpenLedger.h @@ -33,8 +33,8 @@ class OpenLedger private: beast::Journal const j_; CachedSLEs& cache_; - std::mutex mutable modify_mutex_; - std::mutex mutable current_mutex_; + std::mutex mutable modifyMutex_; + std::mutex mutable currentMutex_; std::shared_ptr current_; public: diff --git a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp index 7613aab85a..9f2db9d2f2 100644 --- a/src/xrpld/app/ledger/detail/LedgerCleaner.cpp +++ b/src/xrpld/app/ledger/detail/LedgerCleaner.cpp @@ -286,7 +286,7 @@ private: } Rules const rules{app_.config().features}; - Fees const fees = app_.config().FEES.toFees(); + Fees const fees = app_.config().fees.toFees(); auto const dbLedger = loadByIndex(ledgerIndex, rules, fees, app_); if (!dbLedger || (dbLedger->header().hash != ledgerHash) || (dbLedger->header().parentHash != nodeLedger->header().parentHash)) diff --git a/src/xrpld/app/ledger/detail/LedgerMaster.cpp b/src/xrpld/app/ledger/detail/LedgerMaster.cpp index 50a3a6289c..9baad0ec90 100644 --- a/src/xrpld/app/ledger/detail/LedgerMaster.cpp +++ b/src/xrpld/app/ledger/detail/LedgerMaster.cpp @@ -127,10 +127,10 @@ LedgerMaster::LedgerMaster( , journal_(journal) , ledgerHistory_(collector, app) , standalone_(app_.config().standalone()) - , fetch_depth_(app_.getSHAMapStore().clampFetchDepth(app_.config().FETCH_DEPTH)) - , ledger_history_(app_.config().LEDGER_HISTORY) - , ledger_fetch_size_(app_.config().getValueFor(SizedItem::LedgerFetch)) - , fetch_packs_( + , fetchDepth_(app_.getSHAMapStore().clampFetchDepth(app_.config().fetchDepth)) + , ledgerHistorySize_(app_.config().ledgerHistory) + , ledgerFetchSize_(app_.config().getValueFor(SizedItem::LedgerFetch)) + , fetchPacks_( "FetchPack", 65536, std::chrono::seconds{45}, @@ -286,9 +286,9 @@ LedgerMaster::setValidLedger(std::shared_ptr const& l) validLedgerSign_ = signTime.time_since_epoch().count(); XRPL_ASSERT( validLedgerSeq_ || !app_.getMaxDisallowedLedger() || - l->header().seq + max_ledger_difference_ > app_.getMaxDisallowedLedger(), + l->header().seq + maxLedgerDifference_ > app_.getMaxDisallowedLedger(), "xrpl::LedgerMaster::setValidLedger : valid ledger sequence"); - (void)max_ledger_difference_; + (void)maxLedgerDifference_; validLedgerSeq_ = l->header().seq; app_.getOPs().updateLocalTx(*l); @@ -629,9 +629,9 @@ LedgerMaster::getEarliestFetch() // unless that creates a larger range than allowed std::uint32_t e = getClosedLedger()->header().seq; - if (e > fetch_depth_) + if (e > fetchDepth_) { - e -= fetch_depth_; + e -= fetchDepth_; } else { @@ -1277,10 +1277,10 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl ledger = ledgerHistory_.getLedgerByHash(*hash); } - if (!app_.config().LEDGER_REPLAY) + if (!app_.config().ledgerReplay) { // Can we try to acquire the ledger we need? - if (!ledger && (++acqCount < ledger_fetch_size_)) + if (!ledger && (++acqCount < ledgerFetchSize_)) { ledger = app_.getInboundLedgers().acquire( *hash, seq, InboundLedger::Reason::GENERIC); @@ -1304,7 +1304,7 @@ LedgerMaster::findNewLedgersToPublish(std::unique_lock& sl << ex.what(); } - if (app_.config().LEDGER_REPLAY) + if (app_.config().ledgerReplay) { /* Narrow down the gap of ledgers, and try to replay them. * When replaying a ledger gap, if the local node has @@ -1737,7 +1737,7 @@ void LedgerMaster::sweep() { ledgerHistory_.sweep(); - fetch_packs_.sweep(); + fetchPacks_.sweep(); } float @@ -1789,11 +1789,11 @@ LedgerMaster::fetchForHistory( if (!app_.getInboundLedgers().isFailure(*hash)) { ledger = app_.getInboundLedgers().acquire(*hash, missing, reason); - if (!ledger && missing != fetch_seq_ && + if (!ledger && missing != fetchSeq_ && missing > app_.getNodeStore().earliestLedgerSeq()) { JLOG(journal_.trace()) << "fetchForHistory want fetch pack " << missing; - fetch_seq_ = missing; + fetchSeq_ = missing; getFetchPack(missing, reason); } else @@ -1833,8 +1833,7 @@ LedgerMaster::fetchForHistory( // Do not fetch ledger sequences lower // than the earliest ledger sequence fetchSz = app_.getNodeStore().earliestLedgerSeq(); - fetchSz = - missing >= fetchSz ? std::min(ledger_fetch_size_, (missing - fetchSz) + 1) : 0; + fetchSz = missing >= fetchSz ? std::min(ledgerFetchSize_, (missing - fetchSz) + 1) : 0; try { for (std::uint32_t i = 0; i < fetchSz; ++i) @@ -1903,7 +1902,7 @@ LedgerMaster::doAdvance(std::unique_lock& sl) if ((fillInProgress_ == 0 || *missing > fillInProgress_) && shouldAcquire( validLedgerSeq_, - ledger_history_, + ledgerHistorySize_, app_.getSHAMapStore().minimumOnline(), *missing, journal_)) @@ -1962,16 +1961,16 @@ LedgerMaster::doAdvance(std::unique_lock& sl) void LedgerMaster::addFetchPack(uint256 const& hash, std::shared_ptr data) { - fetch_packs_.canonicalizeReplaceClient(hash, data); + fetchPacks_.canonicalizeReplaceClient(hash, data); } std::optional LedgerMaster::getFetchPack(uint256 const& hash) { Blob data; - if (fetch_packs_.retrieve(hash, data)) + if (fetchPacks_.retrieve(hash, data)) { - fetch_packs_.del(hash, false); + fetchPacks_.del(hash, false); if (hash == sha512Half(makeSlice(data))) return data; } @@ -2170,7 +2169,7 @@ LedgerMaster::makeFetchPack( std::size_t LedgerMaster::getFetchPackCacheSize() const { - return fetch_packs_.getCacheSize(); + return fetchPacks_.getCacheSize(); } // Returns the minimum ledger sequence in SQL database, if any. diff --git a/src/xrpld/app/ledger/detail/OpenLedger.cpp b/src/xrpld/app/ledger/detail/OpenLedger.cpp index 0694e425e9..3bee4b9d13 100644 --- a/src/xrpld/app/ledger/detail/OpenLedger.cpp +++ b/src/xrpld/app/ledger/detail/OpenLedger.cpp @@ -48,26 +48,26 @@ OpenLedger::OpenLedger( bool OpenLedger::empty() const { - std::scoped_lock const lock(modify_mutex_); + std::scoped_lock const lock(modifyMutex_); return current_->txCount() == 0; } std::shared_ptr OpenLedger::current() const { - std::scoped_lock const lock(current_mutex_); + std::scoped_lock const lock(currentMutex_); return current_; } bool OpenLedger::modify(modify_type const& f) { - std::scoped_lock const lock1(modify_mutex_); + std::scoped_lock const lock1(modifyMutex_); auto next = std::make_shared(*current_); auto const changed = f(*next, j_); if (changed) { - std::scoped_lock const lock2(current_mutex_); + std::scoped_lock const lock2(currentMutex_); current_ = std::move(next); } return changed; @@ -96,7 +96,7 @@ OpenLedger::accept( // Block calls to modify, otherwise // new tx going into the open ledger // would get lost. - std::scoped_lock const lock1(modify_mutex_); + std::scoped_lock const lock1(modifyMutex_); // Apply tx from the current open view if (!current_->txs.empty()) { @@ -154,7 +154,7 @@ OpenLedger::accept( } // Switch to the new open view - std::scoped_lock const lock2(current_mutex_); + std::scoped_lock const lock2(currentMutex_); current_ = std::move(next); } diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index 23fa9865ed..a18462f0f7 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -286,14 +286,14 @@ public: return 1; #else - if (config.IO_WORKERS > 0) - return config.IO_WORKERS; + if (config.ioWorkers > 0) + return config.ioWorkers; auto const cores = std::thread::hardware_concurrency(); // Use a single thread when running on under-provisioned systems // or if we are configured to use minimal resources. - if ((cores == 1) || ((config.NODE_SIZE == 0) && (cores == 2))) + if ((cores == 1) || ((config.nodeSize == 0) && (cores == 2))) return 1; // Otherwise, prefer six threads. @@ -316,7 +316,7 @@ public: // PerfLog must be started before any other threads are launched. , perfLog_( perf::makePerfLog( - perf::setupPerfLog(config_->section("perf"), config_->CONFIG_DIR), + perf::setupPerfLog(config_->section("perf"), config_->configDir), *this, logs_->journal("PerfLog"), [this] { signalStop("PerfLog"); })) @@ -326,22 +326,22 @@ public: , jobQueue_( std::make_unique( [](std::unique_ptr const& config) { - if (config->standalone() && !config->FORCE_MULTI_THREAD) + if (config->standalone() && !config->forceMultiThread) return 1; - if (config->WORKERS) - return config->WORKERS; + if (config->workers) + return config->workers; auto count = static_cast(std::thread::hardware_concurrency()); // Be more aggressive about the number of threads to use // for the job queue if the server is configured as // "large" or "huge" if there are enough cores. - if (config->NODE_SIZE >= 4 && count >= 16) + if (config->nodeSize >= 4 && count >= 16) { count = 6 + std::min(count, 8); } - else if (config->NODE_SIZE >= 3 && count >= 8) + else if (config->nodeSize >= 3 && count >= 8) { count = 4 + std::min(count, 6); } @@ -370,16 +370,16 @@ public: std::chrono::minutes(1), stopwatch(), logs_->journal("CachedSLEs")) - , networkIDService_(std::make_unique(config_->NETWORK_ID)) + , networkIDService_(std::make_unique(config_->networkId)) , validatorKeys_(*config_, journal_) , resourceManager_( Resource::makeManager(collectorManager_->collector(), logs_->journal("Resource"))) , nodeStore_(shaMapStore_->makeNodeStore( - config_->PREFETCH_WORKERS > 0 ? config_->PREFETCH_WORKERS : 4)) + config_->prefetchWorkers > 0 ? config_->prefetchWorkers : 4)) , nodeFamily_(*this, *collectorManager_) , orderBookDB_(makeOrderBookDb( *this, - {.pathSearchMax = config_->PATH_SEARCH_MAX, .standalone = config_->standalone()})) + {.pathSearchMax = config_->pathSearchMax, .standalone = config_->standalone()})) , pathRequestManager_( std::make_unique( *this, @@ -415,8 +415,8 @@ public: *this, stopwatch(), config_->standalone(), - config_->NETWORK_QUORUM, - config_->START_VALID, + config_->networkQuorum, + config_->startValid, *jobQueue_, *ledgerMaster_, validatorKeys_, @@ -435,7 +435,7 @@ public: *timeKeeper_, config_->legacy("database_path"), logs_->journal("ValidatorList"), - config_->VALIDATION_QUORUM)) + config_->validationQuorum)) , validatorSites_(std::make_unique(*this)) , serverHandler_(makeServerHandler( *this, @@ -918,7 +918,7 @@ public: { using namespace std::chrono; sweepTimer_.expires_after( - seconds{config_->SWEEP_INTERVAL.value_or( + seconds{config_->sweepInterval.value_or( config_->getValueFor(SizedItem::SweepInterval))}); sweepTimer_.async_wait(std::move(*optionalCountedHandler)); } @@ -1226,7 +1226,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) amendmentTable_ = makeAmendmentTable( *this, - config().AMENDMENT_MAJORITY_TIME, + config().amendmentMajorityTime, supported, upVoted, downVoted, @@ -1235,7 +1235,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) Pathfinder::initPathTable(); - auto const startUp = config_->START_UP; + auto const startUp = config_->startUp; JLOG(journal_.debug()) << "startUp: " << startUp; if (startUp == StartUpType::Fresh) { @@ -1250,13 +1250,13 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) JLOG(journal_.info()) << "Loading specified Ledger"; if (!loadOldLedger( - config_->START_LEDGER, + config_->startLedger, startUp == StartUpType::Replay, startUp == StartUpType::LoadFile, - config_->TRAP_TX_HASH)) + config_->trapTxHash)) { JLOG(journal_.error()) << "The specified ledger could not be loaded."; - if (config_->FAST_LOAD) + if (config_->fastLoad) { // Fall back to syncing from the network, such as // when there's no existing data. @@ -1282,7 +1282,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) startGenesisLedger(); } - if (auto const& forcedRange = config().FORCED_LEDGER_RANGE_PRESENT) + if (auto const& forcedRange = config().forcedLedgerRangePresent) { ledgerMaster_->setLedgerRangePresent(forcedRange->first, forcedRange->second); } @@ -1328,7 +1328,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) localSigningKey, config().section(SECTION_VALIDATORS).values(), config().section(SECTION_VALIDATOR_LIST_KEYS).values(), - config().VALIDATOR_LIST_THRESHOLD)) + config().validatorListThreshold)) { JLOG(journal_.fatal()) << "Invalid entry in validator configuration."; return false; @@ -1399,7 +1399,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) { // Should this message be here, conceptually? In theory this sort // of message, if displayed, should be displayed from PeerFinder. - if (config_->PEER_PRIVATE && config_->IPS_FIXED.empty()) + if (config_->peerPrivate && config_->ipsFixed.empty()) { JLOG(journal_.warn()) << "No outbound peer connections will be made"; } @@ -1659,14 +1659,14 @@ ApplicationImp::fdRequired() const void ApplicationImp::startGenesisLedger() { - std::vector const initialAmendments = (config_->START_UP == StartUpType::Fresh) + std::vector const initialAmendments = (config_->startUp == StartUpType::Fresh) ? amendmentTable_->getDesired() : std::vector{}; std::shared_ptr const genesis = std::make_shared( kCreateGenesis, Rules{config_->features}, - config_->FEES.toFees(), + config_->fees.toFees(), initialAmendments, nodeFamily_); ledgerMaster_->storeLedger(genesis); @@ -1690,7 +1690,7 @@ ApplicationImp::getLastFullLedger() try { auto const [ledger, seq, hash] = - getLatestLedger(Rules{config_->features}, config_->FEES.toFees(), *this); + getLatestLedger(Rules{config_->features}, config_->fees.toFees(), *this); if (!ledger) return ledger; @@ -1802,7 +1802,7 @@ ApplicationImp::loadLedgerFromFile(std::string const& name) } auto loadLedger = std::make_shared( - seq, closeTime, Rules{config_->features}, config_->FEES.toFees(), nodeFamily_); + seq, closeTime, Rules{config_->features}, config_->fees.toFees(), nodeFamily_); loadLedger->setTotalDrops(totalDrops); for (json::UInt index = 0; index < ledger.get().size(); ++index) @@ -1883,7 +1883,7 @@ ApplicationImp::loadOldLedger( if (hash.parseHex(ledgerID)) { loadLedger = - loadByHash(hash, Rules{config_->features}, config_->FEES.toFees(), *this); + loadByHash(hash, Rules{config_->features}, config_->fees.toFees(), *this); if (!loadLedger) { @@ -1912,7 +1912,7 @@ ApplicationImp::loadOldLedger( if (beast::lexicalCastChecked(index, ledgerID)) { loadLedger = - loadByIndex(index, Rules{config_->features}, config_->FEES.toFees(), *this); + loadByIndex(index, Rules{config_->features}, config_->fees.toFees(), *this); } } @@ -1931,7 +1931,7 @@ ApplicationImp::loadOldLedger( loadLedger = loadByHash( replayLedger->header().parentHash, Rules{config_->features}, - config_->FEES.toFees(), + config_->fees.toFees(), *this); if (!loadLedger) { @@ -2078,7 +2078,7 @@ ApplicationImp::loadOldLedger( bool ApplicationImp::serverOkay(std::string& reason) { - if (!config().ELB_SUPPORT) + if (!config().elbSupport) return true; if (isStopping()) diff --git a/src/xrpld/app/main/BasicApp.cpp b/src/xrpld/app/main/BasicApp.cpp index 71138c6517..c530c170cb 100644 --- a/src/xrpld/app/main/BasicApp.cpp +++ b/src/xrpld/app/main/BasicApp.cpp @@ -9,14 +9,14 @@ BasicApp::BasicApp(std::size_t numberOfThreads) { - work_.emplace(boost::asio::make_work_guard(io_context_)); + work_.emplace(boost::asio::make_work_guard(ioContext_)); threads_.reserve(numberOfThreads); for (std::size_t i = 0; i < numberOfThreads; ++i) { threads_.emplace_back([this, i]() { beast::setCurrentThreadName("io svc #" + std::to_string(i)); - this->io_context_.run(); + this->ioContext_.run(); }); } } diff --git a/src/xrpld/app/main/BasicApp.h b/src/xrpld/app/main/BasicApp.h index d55ab858db..5ba7c719e3 100644 --- a/src/xrpld/app/main/BasicApp.h +++ b/src/xrpld/app/main/BasicApp.h @@ -12,7 +12,7 @@ class BasicApp private: std::optional> work_; std::vector threads_; - boost::asio::io_context io_context_; + boost::asio::io_context ioContext_; public: BasicApp(std::size_t numberOfThreads); @@ -21,7 +21,7 @@ public: boost::asio::io_context& getIoContext() { - return io_context_; + return ioContext_; } [[nodiscard]] size_t diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index ed1d3333d8..f470a2d80f 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -627,7 +627,7 @@ run(int argc, char** argv) { throw std::runtime_error("Invalid force_ledger_present_range parameter"); } - config->FORCED_LEDGER_RANGE_PRESENT.emplace(r[0], r[1]); + config->forcedLedgerRangePresent.emplace(r[0], r[1]); } else { @@ -646,7 +646,7 @@ run(int argc, char** argv) if (vm.contains("start")) { - config->START_UP = StartUpType::Fresh; + config->startUp = StartUpType::Fresh; } if (vm.contains("import")) @@ -654,17 +654,17 @@ run(int argc, char** argv) if (vm.contains("ledger")) { - config->START_LEDGER = vm["ledger"].as(); + config->startLedger = vm["ledger"].as(); if (vm.contains("replay")) { - config->START_UP = StartUpType::Replay; + config->startUp = StartUpType::Replay; if (vm.contains("trap_tx_hash")) { uint256 tmp = {}; auto hash = vm["trap_tx_hash"].as(); if (tmp.parseHex(hash)) { - config->TRAP_TX_HASH = tmp; + config->trapTxHash = tmp; } else { @@ -677,17 +677,17 @@ run(int argc, char** argv) } else { - config->START_UP = StartUpType::Load; + config->startUp = StartUpType::Load; } } else if (vm.contains("ledgerfile")) { - config->START_LEDGER = vm["ledgerfile"].as(); - config->START_UP = StartUpType::LoadFile; + config->startLedger = vm["ledgerfile"].as(); + config->startUp = StartUpType::LoadFile; } - else if (vm.contains("load") || config->FAST_LOAD) + else if (vm.contains("load") || config->fastLoad) { - config->START_UP = StartUpType::Load; + config->startUp = StartUpType::Load; } if (vm.contains("trap_tx_hash") && !vm.contains("replay")) @@ -696,20 +696,20 @@ run(int argc, char** argv) return -1; } - if (vm.contains("net") && !config->FAST_LOAD) + if (vm.contains("net") && !config->fastLoad) { - if ((config->START_UP == StartUpType::Load) || (config->START_UP == StartUpType::Replay)) + if ((config->startUp == StartUpType::Load) || (config->startUp == StartUpType::Replay)) { std::cerr << "Net and load/replay options are incompatible" << std::endl; return -1; } - config->START_UP = StartUpType::Network; + config->startUp = StartUpType::Network; } if (vm.contains("valid")) { - config->START_VALID = true; + config->startValid = true; } // Override the RPC destination IP address. This must @@ -747,15 +747,15 @@ run(int argc, char** argv) } } - config->rpc_ip = std::move(*endpoint); + config->rpcIp = std::move(*endpoint); } if (vm.contains("quorum")) { try { - config->VALIDATION_QUORUM = vm["quorum"].as(); - if (config->VALIDATION_QUORUM == std::size_t{}) + config->validationQuorum = vm["quorum"].as(); + if (config->validationQuorum == std::size_t{}) { throw std::domain_error("0"); } diff --git a/src/xrpld/app/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index 0a55c20d29..c4e6c3cdc2 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -135,13 +135,10 @@ FeeVoteImpl::doValidation(Fees const& lastFees, Rules const& rules, STValidation v[sfield] = target; } }; - vote(lastFees.base, target_.reference_fee, "base fee", sfBaseFeeDrops); - vote(lastFees.reserve, target_.account_reserve, "base reserve", sfReserveBaseDrops); + vote(lastFees.base, target_.referenceFee, "base fee", sfBaseFeeDrops); + vote(lastFees.reserve, target_.accountReserve, "base reserve", sfReserveBaseDrops); vote( - lastFees.increment, - target_.owner_reserve, - "reserve increment", - sfReserveIncrementDrops); + lastFees.increment, target_.ownerReserve, "reserve increment", sfReserveIncrementDrops); } else { @@ -162,11 +159,11 @@ FeeVoteImpl::doValidation(Fees const& lastFees, Rules const& rules, STValidation } }; - vote(lastFees.base, target_.reference_fee, to64, "base fee", sfBaseFee); - vote(lastFees.reserve, target_.account_reserve, to32, "base reserve", sfReserveBase); + vote(lastFees.base, target_.referenceFee, to64, "base fee", sfBaseFee); + vote(lastFees.reserve, target_.accountReserve, to32, "base reserve", sfReserveBase); vote( lastFees.increment, - target_.owner_reserve, + target_.ownerReserve, to32, "reserve increment", sfReserveIncrement); @@ -184,11 +181,11 @@ FeeVoteImpl::doVoting( lastClosedLedger && isFlagLedger(lastClosedLedger->seq()), "xrpl::FeeVoteImpl::doVoting : has a flag ledger"); - detail::VotableValue baseFeeVote(lastClosedLedger->fees().base, target_.reference_fee); + detail::VotableValue baseFeeVote(lastClosedLedger->fees().base, target_.referenceFee); - detail::VotableValue baseReserveVote(lastClosedLedger->fees().reserve, target_.account_reserve); + detail::VotableValue baseReserveVote(lastClosedLedger->fees().reserve, target_.accountReserve); - detail::VotableValue incReserveVote(lastClosedLedger->fees().increment, target_.owner_reserve); + detail::VotableValue incReserveVote(lastClosedLedger->fees().increment, target_.ownerReserve); auto const& rules = lastClosedLedger->rules(); if (rules.enabled(featureXRPFees)) diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index d6f9d10f05..12c79b821c 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -327,7 +327,7 @@ public: validatorKeys.keys ? validatorKeys.keys->masterPublicKey : decltype(validatorMasterPK_){}) , ledgerMaster_(ledgerMaster) - , job_queue_(jobQueue) + , jobQueue_(jobQueue) , standalone_(standalone) , minPeerCount_(startValid ? 0 : minPeerCount) , stats_(std::bind(&NetworkOPsImp::collectMetrics, this), collector) @@ -828,7 +828,7 @@ private: ServerFeeSummary lastFeeSummary_; - JobQueue& job_queue_; + JobQueue& jobQueue_; // Whether we are in standalone mode. bool const standalone_; @@ -853,34 +853,34 @@ private: template Stats(Handler const& handler, beast::insight::Collector::ptr const& collector) : hook(collector->makeHook(handler)) - , disconnected_duration( + , disconnectedDuration( collector->makeGauge("State_Accounting", "Disconnected_duration")) - , connected_duration(collector->makeGauge("State_Accounting", "Connected_duration")) - , syncing_duration(collector->makeGauge("State_Accounting", "Syncing_duration")) - , tracking_duration(collector->makeGauge("State_Accounting", "Tracking_duration")) - , full_duration(collector->makeGauge("State_Accounting", "Full_duration")) - , disconnected_transitions( + , connectedDuration(collector->makeGauge("State_Accounting", "Connected_duration")) + , syncingDuration(collector->makeGauge("State_Accounting", "Syncing_duration")) + , trackingDuration(collector->makeGauge("State_Accounting", "Tracking_duration")) + , fullDuration(collector->makeGauge("State_Accounting", "Full_duration")) + , disconnectedTransitions( collector->makeGauge("State_Accounting", "Disconnected_transitions")) - , connected_transitions( + , connectedTransitions( collector->makeGauge("State_Accounting", "Connected_transitions")) - , syncing_transitions(collector->makeGauge("State_Accounting", "Syncing_transitions")) - , tracking_transitions(collector->makeGauge("State_Accounting", "Tracking_transitions")) - , full_transitions(collector->makeGauge("State_Accounting", "Full_transitions")) + , syncingTransitions(collector->makeGauge("State_Accounting", "Syncing_transitions")) + , trackingTransitions(collector->makeGauge("State_Accounting", "Tracking_transitions")) + , fullTransitions(collector->makeGauge("State_Accounting", "Full_transitions")) { } beast::insight::Hook hook; - beast::insight::Gauge disconnected_duration; - beast::insight::Gauge connected_duration; - beast::insight::Gauge syncing_duration; - beast::insight::Gauge tracking_duration; - beast::insight::Gauge full_duration; + beast::insight::Gauge disconnectedDuration; + beast::insight::Gauge connectedDuration; + beast::insight::Gauge syncingDuration; + beast::insight::Gauge trackingDuration; + beast::insight::Gauge fullDuration; - beast::insight::Gauge disconnected_transitions; - beast::insight::Gauge connected_transitions; - beast::insight::Gauge syncing_transitions; - beast::insight::Gauge tracking_transitions; - beast::insight::Gauge full_transitions; + beast::insight::Gauge disconnectedTransitions; + beast::insight::Gauge connectedTransitions; + beast::insight::Gauge syncingTransitions; + beast::insight::Gauge trackingTransitions; + beast::insight::Gauge fullTransitions; }; std::mutex statsMutex_; // Mutex to lock stats_ @@ -990,7 +990,7 @@ NetworkOPsImp::setTimer( // Only start the timer if waitHandlerCounter_ is not yet joined. if (auto optionalCountedHandler = waitHandlerCounter_.wrap([this, onExpire, onError](boost::system::error_code const& e) { - if ((e.value() == boost::system::errc::success) && (!job_queue_.isStopped())) + if ((e.value() == boost::system::errc::success) && (!jobQueue_.isStopped())) { onExpire(); } @@ -1017,7 +1017,7 @@ NetworkOPsImp::setHeartbeatTimer() heartbeatTimer_, consensus_.parms().ledgerGRANULARITY, [this]() { - job_queue_.addJob(JtNetopTimer, "NetHeart", [this]() { processHeartbeatTimer(); }); + jobQueue_.addJob(JtNetopTimer, "NetHeart", [this]() { processHeartbeatTimer(); }); }, [this]() { setHeartbeatTimer(); }); } @@ -1031,7 +1031,7 @@ NetworkOPsImp::setClusterTimer() clusterTimer_, 10s, [this]() { - job_queue_.addJob(JtNetopCluster, "NetCluster", [this]() { processClusterTimer(); }); + jobQueue_.addJob(JtNetopCluster, "NetCluster", [this]() { processClusterTimer(); }); }, [this]() { setClusterTimer(); }); } @@ -1247,7 +1247,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) auto tx = std::make_shared(trans, reason, registry_.get().getApp()); - job_queue_.addJob(JtTransaction, "SubmitTxn", [this, tx]() { + jobQueue_.addJob(JtTransaction, "SubmitTxn", [this, tx]() { auto t = tx; processTransaction(t, false, false, FailHard::No); }); @@ -1312,7 +1312,7 @@ NetworkOPsImp::processTransaction( bool bLocal, FailHard failType) { - auto ev = job_queue_.makeLoadEvent(JtTxnProc, "ProcessTXN"); + auto ev = jobQueue_.makeLoadEvent(JtTxnProc, "ProcessTXN"); // preProcessTransaction can change our pointer if (!preProcessTransaction(transaction)) @@ -1344,7 +1344,7 @@ NetworkOPsImp::doTransactionAsync( if (dispatchState_ == DispatchState::None) { - if (job_queue_.addJob(JtBatch, "TxBatchAsync", [this]() { transactionBatch(); })) + if (jobQueue_.addJob(JtBatch, "TxBatchAsync", [this]() { transactionBatch(); })) { dispatchState_ = DispatchState::Scheduled; } @@ -1389,7 +1389,7 @@ NetworkOPsImp::doTransactionSyncBatch( if (!transactions_.empty()) { // More transactions need to be applied, but by another job. - if (job_queue_.addJob(JtBatch, "TxBatchSync", [this]() { transactionBatch(); })) + if (jobQueue_.addJob(JtBatch, "TxBatchSync", [this]() { transactionBatch(); })) { dispatchState_ = DispatchState::Scheduled; } @@ -1401,7 +1401,7 @@ NetworkOPsImp::doTransactionSyncBatch( void NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) { - auto ev = job_queue_.makeLoadEvent(JtTxnProc, "ProcessTXNSet"); + auto ev = jobQueue_.makeLoadEvent(JtTxnProc, "ProcessTXNSet"); std::vector> candidates; candidates.reserve(set.size()); for (auto const& [_, tx] : set) @@ -2571,7 +2571,7 @@ NetworkOPsImp::recvValidation(std::shared_ptr const& val, std::str // We will always relay trusted validations; if configured, we will // also relay all untrusted validations. - return registry_.get().getApp().config().RELAY_UNTRUSTED_VALIDATIONS == 1 || val->isTrusted(); + return registry_.get().getApp().config().relayUntrustedValidations == 1 || val->isTrusted(); } json::Value @@ -2631,8 +2631,8 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) info[jss::hostid] = getHostId(admin); // domain: if configured with a domain, report it: - if (!registry_.get().getApp().config().SERVER_DOMAIN.empty()) - info[jss::server_domain] = registry_.get().getApp().config().SERVER_DOMAIN; + if (!registry_.get().getApp().config().serverDomain.empty()) + info[jss::server_domain] = registry_.get().getApp().config().serverDomain; info[jss::build_version] = BuildInfo::getVersionString(); @@ -2652,7 +2652,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) // Note: By default the node size is "tiny". When parsing it's an error if the final // NODE_SIZE is over 4 so below code should be safe. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) - switch (registry_.get().getApp().config().NODE_SIZE) + switch (registry_.get().getApp().config().nodeSize) { case 0: info[jss::node_size] = "tiny"; @@ -2787,7 +2787,7 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) // info[jss::consensus] = consensus_.getJson(); if (admin) - info[jss::load] = job_queue_.getJson(); + info[jss::load] = jobQueue_.getJson(); if (auto const netid = registry_.get().getOverlay().networkID()) info[jss::network_id] = static_cast(*netid); @@ -2952,8 +2952,8 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) { // Don't publish admin ports for non-admin users if (!admin && - !(port.admin_nets_v4.empty() && port.admin_nets_v6.empty() && - port.admin_user.empty() && port.admin_password.empty())) + !(port.adminNetsV4.empty() && port.adminNetsV6.empty() && port.adminUser.empty() && + port.adminPassword.empty())) continue; std::vector proto; // NOLINTNEXTLINE(modernize-use-ranges) @@ -3170,14 +3170,14 @@ NetworkOPsImp::reportFeeChange() // only schedule the job if something has changed if (f != lastFeeSummary_) { - job_queue_.addJob(JtClientFeeChange, "PubFee", [this]() { pubServer(); }); + jobQueue_.addJob(JtClientFeeChange, "PubFee", [this]() { pubServer(); }); } } void NetworkOPsImp::reportConsensusStateChange(ConsensusPhase phase) { - job_queue_.addJob(JtClientConsensus, "PubCons", [this, phase]() { pubConsensus(phase); }); + jobQueue_.addJob(JtClientConsensus, "PubCons", [this, phase]() { pubConsensus(phase); }); } inline void @@ -4626,26 +4626,25 @@ NetworkOPsImp::collectMetrics() counters[static_cast(mode)].dur += current; std::scoped_lock const lock(statsMutex_); - stats_.disconnected_duration.set( + stats_.disconnectedDuration.set( counters[static_cast(OperatingMode::DISCONNECTED)].dur.count()); - stats_.connected_duration.set( + stats_.connectedDuration.set( counters[static_cast(OperatingMode::CONNECTED)].dur.count()); - stats_.syncing_duration.set( + stats_.syncingDuration.set( counters[static_cast(OperatingMode::SYNCING)].dur.count()); - stats_.tracking_duration.set( + stats_.trackingDuration.set( counters[static_cast(OperatingMode::TRACKING)].dur.count()); - stats_.full_duration.set(counters[static_cast(OperatingMode::FULL)].dur.count()); + stats_.fullDuration.set(counters[static_cast(OperatingMode::FULL)].dur.count()); - stats_.disconnected_transitions.set( + stats_.disconnectedTransitions.set( counters[static_cast(OperatingMode::DISCONNECTED)].transitions); - stats_.connected_transitions.set( + stats_.connectedTransitions.set( counters[static_cast(OperatingMode::CONNECTED)].transitions); - stats_.syncing_transitions.set( + stats_.syncingTransitions.set( counters[static_cast(OperatingMode::SYNCING)].transitions); - stats_.tracking_transitions.set( + stats_.trackingTransitions.set( counters[static_cast(OperatingMode::TRACKING)].transitions); - stats_.full_transitions.set( - counters[static_cast(OperatingMode::FULL)].transitions); + stats_.fullTransitions.set(counters[static_cast(OperatingMode::FULL)].transitions); } void diff --git a/src/xrpld/app/misc/SHAMapStoreImp.cpp b/src/xrpld/app/misc/SHAMapStoreImp.cpp index 556c6f9239..fc3f71c0cc 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.cpp +++ b/src/xrpld/app/misc/SHAMapStoreImp.cpp @@ -116,7 +116,7 @@ SHAMapStoreImp::SHAMapStoreImp( section.set("cache_mb", std::to_string(config.getValueFor(SizedItem::HashNodeDbCache))); } - if (!section.exists("filter_bits") && (config.NODE_SIZE >= 2)) + if (!section.exists("filter_bits") && (config.nodeSize >= 2)) section.set("filter_bits", "10"); } @@ -148,15 +148,15 @@ SHAMapStoreImp::SHAMapStoreImp( "online_delete must be at least " + std::to_string(minInterval)); } - if (config.LEDGER_HISTORY > deleteInterval_) + if (config.ledgerHistory > deleteInterval_) { Throw( "online_delete must not be less than ledger_history " "(currently " + - std::to_string(config.LEDGER_HISTORY) + ")"); + std::to_string(config.ledgerHistory) + ")"); } - state_db_.init(config, dbName_); + stateDb_.init(config, dbName_); dbPaths(); } } @@ -169,14 +169,14 @@ SHAMapStoreImp::makeNodeStore(int readThreads) if (deleteInterval_ != 0u) { - SavedState state = state_db_.getState(); + SavedState state = stateDb_.getState(); auto writableBackend = makeBackendRotating(state.writableDb); auto archiveBackend = makeBackendRotating(state.archiveDb); if (state.writableDb.empty()) { state.writableDb = writableBackend->getName(); state.archiveDb = archiveBackend->getName(); - state_db_.setState(state); + stateDb_.setState(state); } // Create NodeStore with two backends to allow online deletion of @@ -251,12 +251,12 @@ void SHAMapStoreImp::run() { beast::setCurrentThreadName("SHAMapStore"); - LedgerIndex lastRotated = state_db_.getState().lastRotated; + LedgerIndex lastRotated = stateDb_.getState().lastRotated; netOPs_ = &app_.getOPs(); ledgerMaster_ = &app_.getLedgerMaster(); if (advisoryDelete_) - canDelete_ = state_db_.getCanDelete(); + canDelete_ = stateDb_.getCanDelete(); while (true) { @@ -286,7 +286,7 @@ SHAMapStoreImp::run() if (lastRotated == 0u) { lastRotated = validatedSeq; - state_db_.setLastRotated(lastRotated); + stateDb_.setLastRotated(lastRotated); } bool const readyToRotate = validatedSeq >= lastRotated + deleteInterval_ && @@ -354,7 +354,7 @@ SHAMapStoreImp::run() savedState.writableDb = writableName; savedState.archiveDb = archiveName; savedState.lastRotated = lastRotated; - state_db_.setState(savedState); + stateDb_.setState(savedState); clearCaches(validatedSeq); }); @@ -383,7 +383,7 @@ SHAMapStoreImp::dbPaths() boost::filesystem::create_directories(dbPath); } - SavedState state = state_db_.getState(); + SavedState state = stateDb_.getState(); { auto update = [&dbPath](std::string& sPath) { @@ -403,7 +403,7 @@ SHAMapStoreImp::dbPaths() if (update(state.writableDb)) { update(state.archiveDb); - state_db_.setState(state); + stateDb_.setState(state); } } diff --git a/src/xrpld/app/misc/SHAMapStoreImp.h b/src/xrpld/app/misc/SHAMapStoreImp.h index 1eb2beb561..4f0ce04e0d 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.h +++ b/src/xrpld/app/misc/SHAMapStoreImp.h @@ -66,7 +66,7 @@ private: NodeStore::Scheduler& scheduler_; beast::Journal const journal_; NodeStore::DatabaseRotating* dbRotating_ = nullptr; - SavedStateDB state_db_; + SavedStateDB stateDb_; std::thread thread_; bool stop_ = false; bool healthy_ = true; @@ -113,7 +113,7 @@ public: { if (advisoryDelete_) canDelete_ = seq; - return state_db_.setCanDelete(seq); + return stateDb_.setCanDelete(seq); } bool @@ -127,7 +127,7 @@ public: LedgerIndex getLastRotated() override { - return state_db_.getState().lastRotated; + return stateDb_.getState().lastRotated; } // All ledgers before and including this are unprotected diff --git a/src/xrpld/app/misc/ValidatorSite.h b/src/xrpld/app/misc/ValidatorSite.h index c4d2545217..55f060baf1 100644 --- a/src/xrpld/app/misc/ValidatorSite.h +++ b/src/xrpld/app/misc/ValidatorSite.h @@ -96,10 +96,10 @@ private: Application& app_; beast::Journal const j_; - // If both mutex are to be locked at the same time, `sites_mutex_` must be - // locked before `state_mutex_` or we may deadlock. - std::mutex mutable sites_mutex_; - std::mutex mutable state_mutex_; + // If both mutex are to be locked at the same time, `sitesMutex_` must be + // locked before `stateMutex_` or we may deadlock. + std::mutex mutable sitesMutex_; + std::mutex mutable stateMutex_; std::condition_variable cv_; std::weak_ptr work_; diff --git a/src/xrpld/app/misc/detail/ValidatorSite.cpp b/src/xrpld/app/misc/detail/ValidatorSite.cpp index 7b5d7ee951..76fd078174 100644 --- a/src/xrpld/app/misc/detail/ValidatorSite.cpp +++ b/src/xrpld/app/misc/detail/ValidatorSite.cpp @@ -114,7 +114,7 @@ ValidatorSite::ValidatorSite( ValidatorSite::~ValidatorSite() { - std::unique_lock lock{state_mutex_}; + std::unique_lock lock{stateMutex_}; if (timer_.expiry() > clock_type::time_point{}) { if (!stopping_) @@ -141,7 +141,7 @@ ValidatorSite::load(std::vector const& siteURIs) { JLOG(j_.debug()) << "Loading configured validator list sites"; - std::scoped_lock const lock{sites_mutex_}; + std::scoped_lock const lock{sitesMutex_}; return load(siteURIs, lock); } @@ -178,8 +178,8 @@ ValidatorSite::load( void ValidatorSite::start() { - std::scoped_lock const l0{sites_mutex_}; - std::scoped_lock const l1{state_mutex_}; + std::scoped_lock const l0{sitesMutex_}; + std::scoped_lock const l1{stateMutex_}; if (timer_.expiry() == clock_type::time_point{}) setTimer(l0, l1); } @@ -187,14 +187,14 @@ ValidatorSite::start() void ValidatorSite::join() { - std::unique_lock lock{state_mutex_}; + std::unique_lock lock{stateMutex_}; cv_.wait(lock, [&] { return !pending_; }); } void ValidatorSite::stop() { - std::unique_lock lock{state_mutex_}; + std::unique_lock lock{stateMutex_}; stopping_ = true; // work::cancel() must be called before the // cv wait in order to kick any asio async operations @@ -246,7 +246,7 @@ ValidatorSite::makeRequest( sites_[siteIdx].activeResource = resource; std::shared_ptr sp; auto timeoutCancel = [this]() { - std::scoped_lock const lockState{state_mutex_}; + std::scoped_lock const lockState{stateMutex_}; // docs indicate cancel_one() can throw, but this // should be reconsidered if it changes to noexcept try @@ -312,7 +312,7 @@ ValidatorSite::makeRequest( sp->run(); // start a timer for the request, which shouldn't take more // than requestTimeout_ to complete - std::scoped_lock const lockState{state_mutex_}; + std::scoped_lock const lockState{stateMutex_}; timer_.expires_after(requestTimeout_); timer_.async_wait([this, siteIdx](boost::system::error_code const& ec) { this->onRequestTimeout(siteIdx, ec); @@ -326,7 +326,7 @@ ValidatorSite::onRequestTimeout(std::size_t siteIdx, error_code const& ec) return; { - std::scoped_lock const lockSite{sites_mutex_}; + std::scoped_lock const lockSite{sitesMutex_}; // In some circumstances, both this function and the response // handler (onSiteFetch or onTextFetch) can get queued and // processed. In all observed cases, the response handler @@ -343,7 +343,7 @@ ValidatorSite::onRequestTimeout(std::size_t siteIdx, error_code const& ec) "already been processed"; } - std::scoped_lock const lockState{state_mutex_}; + std::scoped_lock const lockState{stateMutex_}; if (auto sp = work_.lock()) sp->cancel(); } @@ -362,7 +362,7 @@ ValidatorSite::onTimer(std::size_t siteIdx, error_code const& ec) try { - std::scoped_lock const lock{sites_mutex_}; + std::scoped_lock const lock{sitesMutex_}; sites_[siteIdx].nextRefresh = clock_type::now() + sites_[siteIdx].refreshInterval; sites_[siteIdx].redirCount = 0; // the WorkSSL client ctor can throw if SSL init fails @@ -545,7 +545,7 @@ ValidatorSite::onSiteFetch( detail::response_type const& res, std::size_t siteIdx) { - std::scoped_lock lockSites{sites_mutex_}; + std::scoped_lock lockSites{sitesMutex_}; { if (endpoint != endpoint_type{}) sites_[siteIdx].lastRequestEndpoint = endpoint; @@ -617,7 +617,7 @@ ValidatorSite::onSiteFetch( sites_[siteIdx].activeResource.reset(); } - std::scoped_lock const lockState{state_mutex_}; + std::scoped_lock const lockState{stateMutex_}; fetching_ = false; if (!stopping_) setTimer(lockSites, lockState); @@ -630,7 +630,7 @@ ValidatorSite::onTextFetch( std::string const& res, std::size_t siteIdx) { - std::scoped_lock const lockSites{sites_mutex_}; + std::scoped_lock const lockSites{sitesMutex_}; { try { @@ -657,7 +657,7 @@ ValidatorSite::onTextFetch( sites_[siteIdx].activeResource.reset(); } - std::scoped_lock const lockState{state_mutex_}; + std::scoped_lock const lockState{stateMutex_}; fetching_ = false; if (!stopping_) setTimer(lockSites, lockState); @@ -673,7 +673,7 @@ ValidatorSite::getJson() const json::Value jrr(json::ValueType::Object); json::Value& jSites = (jrr[jss::validator_sites] = json::ValueType::Array); { - std::scoped_lock const lock{sites_mutex_}; + std::scoped_lock const lock{sitesMutex_}; for (Site const& site : sites_) { json::Value& v = jSites.append(json::ValueType::Object); diff --git a/src/xrpld/app/misc/detail/WorkSSL.cpp b/src/xrpld/app/misc/detail/WorkSSL.cpp index 0a8d53b1a2..e1b864e81f 100644 --- a/src/xrpld/app/misc/detail/WorkSSL.cpp +++ b/src/xrpld/app/misc/detail/WorkSSL.cpp @@ -30,9 +30,9 @@ WorkSSL::WorkSSL( callback_type cb) : WorkBase(host, path, port, ios, lastEndpoint, lastStatus, cb) , context_( - config.SSL_VERIFY_DIR, - config.SSL_VERIFY_FILE, - config.SSL_VERIFY, + config.sslVerifyDir, + config.sslVerifyFile, + config.sslVerify, j, boost::asio::ssl::context::tlsv12_client) , stream_(socket_, context_.context()) diff --git a/src/xrpld/consensus/Consensus.cpp b/src/xrpld/consensus/Consensus.cpp index 5498f7cf79..d529ab2e44 100644 --- a/src/xrpld/consensus/Consensus.cpp +++ b/src/xrpld/consensus/Consensus.cpp @@ -35,7 +35,7 @@ shouldCloseLedger( << ", timeSincePrevClose: " << timeSincePrevClose.count() << "ms" << ", openTime: " << openTime.count() << "ms" << ", idleInterval: " << idleInterval.count() << "ms" - << ", ledgerMIN_CLOSE: " << parms.ledgerMIN_CLOSE.count() << "ms" + << ", ledgerMIN_CLOSE: " << parms.ledgerMinClose.count() << "ms" << ". "; using namespace std::chrono_literals; if ((prevRoundTime < -1s) || (prevRoundTime > 10min) || (timeSincePrevClose > 10min)) @@ -67,7 +67,7 @@ shouldCloseLedger( } // Preserve minimum ledger open time - if (openTime < parms.ledgerMIN_CLOSE) + if (openTime < parms.ledgerMinClose) { JLOG(j.debug()) << "Must wait minimum time before closing"; CLOG(clog) << "not closing because under ledgerMIN_CLOSE. "; @@ -175,12 +175,12 @@ checkConsensus( << " agree=" << currentAgree << " validated=" << currentFinished << " time=" << currentAgreeTime.count() << "/" << previousAgreeTime.count() << " proposing? " << proposing - << " minimum duration to reach consensus: " << parms.ledgerMIN_CONSENSUS.count() + << " minimum duration to reach consensus: " << parms.ledgerMinConsensus.count() << "ms" - << " max consensus time " << parms.ledgerMAX_CONSENSUS.count() << "ms" - << " minimum consensus percentage: " << parms.minCONSENSUS_PCT << ". "; + << " max consensus time " << parms.ledgerMaxConsensus.count() << "ms" + << " minimum consensus percentage: " << parms.minConsensusPct << ". "; - if (currentAgreeTime <= parms.ledgerMIN_CONSENSUS) + if (currentAgreeTime <= parms.ledgerMinConsensus) { CLOG(clog) << "Not reached. "; return ConsensusState::No; @@ -190,7 +190,7 @@ checkConsensus( { // Less than 3/4 of the last ledger's proposers are present; don't // rush: we may need more time. - if (currentAgreeTime < (previousAgreeTime + parms.ledgerMIN_CONSENSUS)) + if (currentAgreeTime < (previousAgreeTime + parms.ledgerMinConsensus)) { JLOG(j.trace()) << "too fast, not enough proposers"; CLOG(clog) << "Too fast, not enough proposers. Not reached. "; @@ -204,8 +204,8 @@ checkConsensus( currentAgree, currentProposers, proposing, - parms.minCONSENSUS_PCT, - currentAgreeTime > parms.ledgerMAX_CONSENSUS, + parms.minConsensusPct, + currentAgreeTime > parms.ledgerMaxConsensus, stalled, clog)) { @@ -221,8 +221,8 @@ checkConsensus( currentFinished, currentProposers, false, - parms.minCONSENSUS_PCT, - currentAgreeTime > parms.ledgerMAX_CONSENSUS, + parms.minConsensusPct, + currentAgreeTime > parms.ledgerMaxConsensus, false, clog)) { @@ -232,9 +232,9 @@ checkConsensus( } std::chrono::milliseconds const maxAgreeTime = - previousAgreeTime * parms.ledgerABANDON_CONSENSUS_FACTOR; + previousAgreeTime * parms.ledgerAbandonConsensusFactor; if (currentAgreeTime > - std::clamp(maxAgreeTime, parms.ledgerMAX_CONSENSUS, parms.ledgerABANDON_CONSENSUS)) + std::clamp(maxAgreeTime, parms.ledgerMaxConsensus, parms.ledgerAbandonConsensus)) { JLOG(j.warn()) << "consensus taken too long"; CLOG(clog) << "Consensus taken too long. "; diff --git a/src/xrpld/consensus/Consensus.h b/src/xrpld/consensus/Consensus.h index 8b4e971440..131db30ce0 100644 --- a/src/xrpld/consensus/Consensus.h +++ b/src/xrpld/consensus/Consensus.h @@ -625,7 +625,7 @@ Consensus::startRound( if (firstRound_) { // take our initial view of closeTime_ from the seed ledger - prevRoundTime_ = adaptor_.parms().ledgerIDLE_INTERVAL; + prevRoundTime_ = adaptor_.parms().ledgerIdleInterval; prevCloseTime_ = prevLedger.closeTime(); firstRound_ = false; } @@ -1169,9 +1169,9 @@ Consensus::phaseOpen(std::unique_ptr const& clog) } auto const idleInterval = std::max( - adaptor_.parms().ledgerIDLE_INTERVAL, 2 * previousLedger_.closeTimeResolution()); + adaptor_.parms().ledgerIdleInterval, 2 * previousLedger_.closeTimeResolution()); CLOG(clog) << "idle interval set to " << idleInterval.count() << "ms based on " - << "ledgerIDLE_INTERVAL: " << adaptor_.parms().ledgerIDLE_INTERVAL.count() + << "ledgerIDLE_INTERVAL: " << adaptor_.parms().ledgerIdleInterval.count() << ", previous ledger close time resolution: " << previousLedger_.closeTimeResolution().count() << "ms. "; @@ -1218,7 +1218,7 @@ Consensus::shouldPause(std::unique_ptr const& clog) << "roundTime: " << result_->roundTime.read().count() << ", " // NOLINTEND(bugprone-unchecked-optional-access) - << "max consensus time: " << parms.ledgerMAX_CONSENSUS.count() << ", " + << "max consensus time: " << parms.ledgerMaxConsensus.count() << ", " << "validators: " << totalValidators << ", " << "laggards: " << laggards << ", " << "offline: " << offline << ", " @@ -1227,7 +1227,7 @@ Consensus::shouldPause(std::unique_ptr const& clog) if ((ahead == 0u) || (laggards == 0u) || (totalValidators == 0u) || !adaptor_.validator() || !adaptor_.haveValidated() || // NOLINTNEXTLINE(bugprone-unchecked-optional-access) result_ set as shouldPause called - result_->roundTime.read() > parms.ledgerMAX_CONSENSUS) + result_->roundTime.read() > parms.ledgerMaxConsensus) { j_.debug() << "not pausing (early)" << vars.str(); CLOG(clog) << "Not pausing (early). "; @@ -1337,17 +1337,17 @@ Consensus::phaseEstablish(std::unique_ptr const& clo result_->proposers = currPeerPositions_.size(); convergePercent_ = result_->roundTime.read() * 100 / - std::max(prevRoundTime_, parms.avMIN_CONSENSUS_TIME); + std::max(prevRoundTime_, parms.avMinConsensusTime); CLOG(clog) << "convergePercent_ " << convergePercent_ << " is based on round duration so far: " << result_->roundTime.read().count() << "ms, " << "previous round duration: " << prevRoundTime_.count() << "ms, " - << "avMIN_CONSENSUS_TIME: " << parms.avMIN_CONSENSUS_TIME.count() << "ms. "; + << "avMIN_CONSENSUS_TIME: " << parms.avMinConsensusTime.count() << "ms. "; // Give everyone a chance to take an initial position - if (result_->roundTime.read() < parms.ledgerMIN_CONSENSUS) + if (result_->roundTime.read() < parms.ledgerMinConsensus) { - CLOG(clog) << "ledgerMIN_CONSENSUS not reached: " << parms.ledgerMIN_CONSENSUS.count() + CLOG(clog) << "ledgerMIN_CONSENSUS not reached: " << parms.ledgerMinConsensus.count() << "ms. "; return; } @@ -1542,7 +1542,7 @@ Consensus::updateOurPositions(std::unique_ptr const& int threshVote = participantsNeeded(participants, neededWeight); // Threshold to declare consensus - int const threshConsensus = participantsNeeded(participants, parms.avCT_CONSENSUS_PCT); + int const threshConsensus = participantsNeeded(participants, parms.avCtConsensusPct); std::stringstream ss; ss << "Proposers:" << currPeerPositions_.size() << " nw:" << neededWeight @@ -1696,7 +1696,7 @@ Consensus::haveConsensus(std::unique_ptr const& clog // Consensus has taken far too long. Drop out of the round. if (result_->state == ConsensusState::Expired) { - static auto const kMinimumCounter = parms.avalancheCutoffs.size() * parms.avMIN_ROUNDS; + static auto const kMinimumCounter = parms.avalancheCutoffs.size() * parms.avMinRounds; std::stringstream ss; if (establishCounter_ < kMinimumCounter) { diff --git a/src/xrpld/consensus/ConsensusParms.h b/src/xrpld/consensus/ConsensusParms.h index 88a6318b3c..e6dd7f046e 100644 --- a/src/xrpld/consensus/ConsensusParms.h +++ b/src/xrpld/consensus/ConsensusParms.h @@ -28,7 +28,7 @@ struct ConsensusParms 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 validationVALID_WALL = std::chrono::minutes{5}; + std::chrono::seconds const validationValidWall = std::chrono::minutes{5}; /** Duration a validation remains current after first observed. @@ -36,14 +36,14 @@ struct ConsensusParms 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 validationVALID_LOCAL = std::chrono::minutes{3}; + 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 */ - std::chrono::seconds const validationVALID_EARLY = std::chrono::minutes{3}; + std::chrono::seconds const validationValidEarly = std::chrono::minutes{3}; //! How long we consider a proposal fresh std::chrono::seconds const proposeFRESHNESS = std::chrono::seconds{20}; @@ -56,13 +56,13 @@ struct ConsensusParms // millisecond resolution. //! The percentage threshold above which we can declare consensus. - std::size_t const minCONSENSUS_PCT = 80; + std::size_t const minConsensusPct = 80; //! The duration a ledger may remain idle before closing - std::chrono::milliseconds const ledgerIDLE_INTERVAL = std::chrono::seconds{15}; + std::chrono::milliseconds const ledgerIdleInterval = std::chrono::seconds{15}; //! The number of seconds we wait minimum to ensure participation - std::chrono::milliseconds const ledgerMIN_CONSENSUS = std::chrono::milliseconds{1950}; + std::chrono::milliseconds const ledgerMinConsensus = std::chrono::milliseconds{1950}; /** The maximum amount of time to spend pausing for laggards. * @@ -70,16 +70,16 @@ struct ConsensusParms * validators don't appear to be offline that are merely waiting for * laggards. */ - std::chrono::milliseconds const ledgerMAX_CONSENSUS = std::chrono::seconds{15}; + std::chrono::milliseconds const ledgerMaxConsensus = std::chrono::seconds{15}; //! Minimum number of seconds to wait to ensure others have computed the LCL - std::chrono::milliseconds const ledgerMIN_CLOSE = std::chrono::seconds{2}; + std::chrono::milliseconds const ledgerMinClose = std::chrono::seconds{2}; //! 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 - std::size_t const ledgerABANDON_CONSENSUS_FACTOR = 10; + std::size_t const ledgerAbandonConsensusFactor = 10; /** * Maximum amount of time to give a consensus round @@ -87,7 +87,7 @@ struct ConsensusParms * Does not include the time to build the LCL, so there is no reason for a * round to go this long, regardless of how big the ledger is. */ - std::chrono::milliseconds const ledgerABANDON_CONSENSUS = std::chrono::seconds{120}; + std::chrono::milliseconds const ledgerAbandonConsensus = std::chrono::seconds{120}; /** The minimum amount of time to consider the previous round to have taken. @@ -99,7 +99,7 @@ struct ConsensusParms twice the interval between proposals (0.7s) divided by the interval between mid and late consensus ([85-50]/100). */ - std::chrono::milliseconds const avMIN_CONSENSUS_TIME = std::chrono::seconds{5}; + std::chrono::milliseconds const avMinConsensusTime = std::chrono::seconds{5}; //------------------------------------------------------------------------------ // Avalanche tuning @@ -136,16 +136,16 @@ struct ConsensusParms }; //! Percentage of nodes required to reach agreement on ledger close time - std::size_t const avCT_CONSENSUS_PCT = 75; + std::size_t const avCtConsensusPct = 75; //! 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 avMIN_ROUNDS = 2; + std::size_t const avMinRounds = 2; //! Number of rounds before a stuck vote is considered unlikely to change //! because voting stalled - std::size_t const avSTALLED_ROUNDS = 4; + std::size_t const avStalledRounds = 4; }; inline std::pair> diff --git a/src/xrpld/consensus/DisputedTx.h b/src/xrpld/consensus/DisputedTx.h index e888b95ed6..1c85a3537d 100644 --- a/src/xrpld/consensus/DisputedTx.h +++ b/src/xrpld/consensus/DisputedTx.h @@ -79,20 +79,20 @@ public: // enough, so there's room for change. Check the times in case the state // machine is altered to allow states to loop. if (nextCutoff.consensusTime > currentCutoff.consensusTime || - avalancheCounter_ < p.avMIN_ROUNDS) + avalancheCounter_ < p.avMinRounds) return false; // We've haven't had this vote for minimum rounds yet. Things could // change. - if (proposing && currentVoteCounter_ < p.avMIN_ROUNDS) + if (proposing && currentVoteCounter_ < p.avMinRounds) return false; // If we or any peers have changed a vote in several rounds, then // things could still change. But if _either_ has not changed in that // long, we're unlikely to change our vote any time soon. (This prevents // a malicious peer from flip-flopping a vote to prevent consensus.) - if (peersUnchanged < p.avSTALLED_ROUNDS && - (proposing && currentVoteCounter_ < p.avSTALLED_ROUNDS)) + if (peersUnchanged < p.avStalledRounds && + (proposing && currentVoteCounter_ < p.avStalledRounds)) return false; // Does this transaction have more than 80% agreement @@ -108,7 +108,7 @@ public: int const weight = support / total; // Returns true if the tx has more than minCONSENSUS_PCT (80) percent // agreement. Either voting for _or_ voting against the tx. - bool const stalled = weight > p.minCONSENSUS_PCT || weight < (100 - p.minCONSENSUS_PCT); + bool const stalled = weight > p.minConsensusPct || weight < (100 - p.minConsensusPct); if (stalled) { @@ -276,7 +276,7 @@ DisputedTx::updateVote(int percentTime, bool proposing, ConsensusPar // Proposing or not, we need to keep track of which state we've reached so // we can determine if the vote has stalled. auto const [requiredPct, newState] = - getNeededWeight(p, avalancheState_, percentTime, ++avalancheCounter_, p.avMIN_ROUNDS); + getNeededWeight(p, avalancheState_, percentTime, ++avalancheCounter_, p.avMinRounds); if (newState) { avalancheState_ = *newState; diff --git a/src/xrpld/consensus/Validations.h b/src/xrpld/consensus/Validations.h index fd32b52518..7be578060e 100644 --- a/src/xrpld/consensus/Validations.h +++ b/src/xrpld/consensus/Validations.h @@ -33,7 +33,7 @@ struct ValidationParms 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 validationCURRENT_WALL = std::chrono::minutes{5}; + std::chrono::seconds validationCurrentWall = std::chrono::minutes{5}; /** Duration a validation remains current after first observed. @@ -41,14 +41,14 @@ struct ValidationParms 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 validationCURRENT_LOCAL = std::chrono::minutes{3}; + 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 */ - std::chrono::seconds validationCURRENT_EARLY = std::chrono::minutes{3}; + std::chrono::seconds validationCurrentEarly = std::chrono::minutes{3}; /** Duration a set of validations for a given ledger hash remain valid @@ -56,7 +56,7 @@ struct ValidationParms hash can expire. This keeps validations for recent ledgers available for a reasonable interval. */ - std::chrono::seconds validationSET_EXPIRES = std::chrono::minutes{10}; + std::chrono::seconds validationSetExpires = std::chrono::minutes{10}; /** How long we consider a validation fresh. * @@ -98,7 +98,7 @@ public: bool operator()(time_point now, Seq s, ValidationParms const& p) { - if (now > (when_ + p.validationSET_EXPIRES)) + if (now > (when_ + p.validationSetExpires)) seq_ = Seq{0}; if (s <= seq_) return false; @@ -139,9 +139,9 @@ isCurrent( // promoted from unsigned 32 bit to signed 64 bit prior // to computation. - return (signTime > (now - p.validationCURRENT_EARLY)) && - (signTime < (now + p.validationCURRENT_WALL)) && - ((seenTime == NetClock::time_point{}) || (seenTime < (now + p.validationCURRENT_LOCAL))); + return (signTime > (now - p.validationCurrentEarly)) && + (signTime < (now + p.validationCurrentWall)) && + ((seenTime == NetClock::time_point{}) || (seenTime < (now + p.validationCurrentLocal))); } /** Status of validation we received */ @@ -610,7 +610,7 @@ public: auto const diff = std::max(seqit->second.signTime(), val.signTime()) - std::min(seqit->second.signTime(), val.signTime()); - if (diff > parms_.validationCURRENT_WALL && + if (diff > parms_.validationCurrentWall && val.signTime() > seqit->second.signTime()) seqit->second = val; } @@ -707,7 +707,7 @@ public: { // The next refresh time is shortly before the expiration // time from now. - kRefreshTime = now + parms_.validationSET_EXPIRES - parms_.validationFRESHNESS; + kRefreshTime = now + parms_.validationSetExpires - parms_.validationFRESHNESS; for (auto i = byLedger_.begin(); i != byLedger_.end(); ++i) { @@ -732,8 +732,8 @@ public: } } - beast::expire(byLedger_, parms_.validationSET_EXPIRES); - beast::expire(bySequence_, parms_.validationSET_EXPIRES); + beast::expire(byLedger_, parms_.validationSetExpires); + beast::expire(bySequence_, parms_.validationSetExpires); } JLOG(j.debug()) << "Validations sets sweep lock duration " << std::chrono::duration_cast( diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index 322683e119..a18b68a508 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -48,13 +48,13 @@ A default-constructed Setup contains recommended values. struct FeeSetup { /** The cost of a reference transaction in drops. */ - XRPAmount reference_fee{10}; + XRPAmount referenceFee{10}; /** The account reserve requirement in drops. */ - XRPAmount account_reserve{10 * kDropsPerXrp}; + XRPAmount accountReserve{10 * kDropsPerXrp}; /** The per-owned item reserve requirement in drops. */ - XRPAmount owner_reserve{2 * kDropsPerXrp}; + XRPAmount ownerReserve{2 * kDropsPerXrp}; /* (Remember to update the example cfg files when changing any of these * values.) */ @@ -63,7 +63,7 @@ struct FeeSetup [[nodiscard]] Fees toFees() const { - return Fees{reference_fee, account_reserve, owner_reserve}; + return Fees{referenceFee, accountReserve, ownerReserve}; } }; @@ -86,20 +86,20 @@ public: getDebugLogFile() const; private: - boost::filesystem::path CONFIG_FILE_; + boost::filesystem::path configFile_; public: - boost::filesystem::path CONFIG_DIR; + boost::filesystem::path configDir; private: - boost::filesystem::path DEBUG_LOGFILE_; + boost::filesystem::path debugLogfile_; void load(); beast::Journal const j_; - bool QUIET_ = false; // Minimize logging verbosity. - bool SILENT_ = false; // No output to console after startup. + bool quiet_ = false; // Minimize logging verbosity. + bool silent_ = false; // No output to console after startup. /** Operate in stand-alone mode. In stand alone mode: @@ -109,9 +109,9 @@ private: - If no ledger is loaded, the default ledger with the root account is created. */ - bool RUN_STANDALONE_ = false; + bool runStandalone_ = false; - bool USE_TX_TABLES_ = true; + bool useTxTables_ = true; /** Determines if the server will sign a tx, given an account's secret seed. @@ -126,45 +126,45 @@ private: public: bool doImport = false; - bool ELB_SUPPORT = false; + bool elbSupport = false; // Entries from [ips] config stanza - std::vector IPS; + std::vector ips; // Entries from [ips_fixed] config stanza - std::vector IPS_FIXED; + std::vector ipsFixed; - StartUpType START_UP = StartUpType::Normal; + StartUpType startUp = StartUpType::Normal; - bool START_VALID = false; + bool startValid = false; - std::string START_LEDGER; + std::string startLedger; - std::optional TRAP_TX_HASH; + std::optional trapTxHash; // Network parameters - uint32_t NETWORK_ID = 0; + uint32_t networkId = 0; // Note: The following parameters do not relate to the UNL or trust at all // Minimum number of nodes to consider the network present - std::size_t NETWORK_QUORUM = 1; + std::size_t networkQuorum = 1; // Peer networking parameters // 1 = relay, 0 = do not relay (but process), -1 = drop completely (do NOT // process) - int RELAY_UNTRUSTED_VALIDATIONS = 1; - int RELAY_UNTRUSTED_PROPOSALS = 0; + int relayUntrustedValidations = 1; + int relayUntrustedProposals = 0; // True to ask peers not to relay current IP. - bool PEER_PRIVATE = false; + bool peerPrivate = false; // peers_max is a legacy configuration, which is going to be replaced // with individual inbound peers peers_in_max and outbound peers // peers_out_max configuration. for now we support both the legacy and // the new configuration. if peers_max is configured then peers_in_max and // peers_out_max are ignored. - std::size_t PEERS_MAX = 0; - std::size_t PEERS_OUT_MAX = 0; - std::size_t PEERS_IN_MAX = 0; + std::size_t peersMax = 0; + std::size_t peersOutMax = 0; + std::size_t peersInMax = 0; // Path searching: these were reasonable default values at some point but // further research is needed to decide if they still are @@ -178,110 +178,110 @@ public: // Servers operating as validators disable path finding by // default by setting the `PATH_SEARCH_MAX` option to 0 // unless it is explicitly set in the configuration file. - int PATH_SEARCH_OLD = 2; - int PATH_SEARCH = 2; - int PATH_SEARCH_FAST = 2; - int PATH_SEARCH_MAX = 3; + int pathSearchOld = 2; + int pathSearch = 2; + int pathSearchFast = 2; + int pathSearchMax = 3; // Validation - std::optional VALIDATION_QUORUM; // validations to consider ledger authoritative + std::optional validationQuorum; // validations to consider ledger authoritative - FeeSetup FEES; + FeeSetup fees; // Node storage configuration - std::uint32_t LEDGER_HISTORY = 256; - std::uint32_t FETCH_DEPTH = 1000000000; + std::uint32_t ledgerHistory = 256; + std::uint32_t fetchDepth = 1000000000; // Tunable that adjusts various parameters, typically associated // with hardware parameters (RAM size and CPU cores). The default // is 'tiny'. - std::size_t NODE_SIZE = 0; + std::size_t nodeSize = 0; - bool SSL_VERIFY = true; - std::string SSL_VERIFY_FILE; - std::string SSL_VERIFY_DIR; + bool sslVerify = true; + std::string sslVerifyFile; + std::string sslVerifyDir; // Compression - bool COMPRESSION = false; + bool compression = false; // Enable the experimental Ledger Replay functionality - bool LEDGER_REPLAY = false; + bool ledgerReplay = false; // Work queue limits - int MAX_TRANSACTIONS = 250; + int maxTransactions = 250; static constexpr int kMaxJobQueueTx = 1000; static constexpr int kMinJobQueueTx = 100; // Amendment majority time - std::chrono::seconds AMENDMENT_MAJORITY_TIME = kDefaultAmendmentMajorityTime; + std::chrono::seconds amendmentMajorityTime = kDefaultAmendmentMajorityTime; // Thread pool configuration (0 = choose for me) - int WORKERS = 0; // jobqueue thread count. default: upto 6 - int IO_WORKERS = 0; // io svc thread count. default: 2 - int PREFETCH_WORKERS = 0; // prefetch thread count. default: 4 + int workers = 0; // jobqueue thread count. default: upto 6 + int ioWorkers = 0; // io svc thread count. default: 2 + int prefetchWorkers = 0; // prefetch thread count. default: 4 // Can only be set in code, specifically unit tests - bool FORCE_MULTI_THREAD = false; + bool forceMultiThread = false; // Normally the sweep timer is automatically deduced based on the node // size, but we allow admins to explicitly set it in the config. - std::optional SWEEP_INTERVAL; + std::optional sweepInterval; // Reduce-relay - Experimental parameters to control p2p routing algorithms // Enable base squelching of duplicate validation/proposal messages - bool VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = false; + bool vpReduceRelayBaseSquelchEnable = false; ///////////////////// !!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 VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS = 5; + std::size_t vpReduceRelaySquelchMaxSelectedPeers = 5; ///////////////// END OF TEMPORARY CODE BLOCK ///////////////////// // Transaction reduce-relay feature - bool TX_REDUCE_RELAY_ENABLE = false; + bool txReduceRelayEnable = false; // If tx reduce-relay feature is disabled // and this flag is enabled then some // tx-related metrics is collected. It // is ignored if tx reduce-relay feature is // enabled. It is used in debugging to compare // metrics with the feature disabled/enabled. - bool TX_REDUCE_RELAY_METRICS = false; + bool txReduceRelayMetrics = false; // Minimum peers a server should have before // selecting random peers - std::size_t TX_REDUCE_RELAY_MIN_PEERS = 20; + std::size_t txReduceRelayMinPeers = 20; // Percentage of peers with the tx reduce-relay feature enabled // to relay to out of total active peers - std::size_t TX_RELAY_PERCENTAGE = 25; + std::size_t txRelayPercentage = 25; // These override the command line client settings - std::optional rpc_ip; + std::optional rpcIp; std::unordered_set> features; - std::string SERVER_DOMAIN; + std::string serverDomain; // How long can a peer remain in the "unknown" state - std::chrono::seconds MAX_UNKNOWN_TIME{600}; + std::chrono::seconds maxUnknownTime{600}; // How long can a peer remain in the "diverged" state - std::chrono::seconds MAX_DIVERGED_TIME{300}; + std::chrono::seconds maxDivergedTime{300}; // Enable the beta API version - bool BETA_RPC_API = false; + bool betaRpcApi = false; // First, attempt to load the latest ledger directly from disk. - bool FAST_LOAD = false; + bool fastLoad = false; // When starting xrpld with existing database it do not know it has those // ledgers locally until the server naturally tries to backfill. This makes // is difficult to test some functionality (in particular performance // testing sidechains). With this variable the user is able to force xrpld // to consider the ledger range to be present. It should be used for testing // only. - std::optional> FORCED_LEDGER_RANGE_PRESENT; + std::optional> forcedLedgerRangePresent; - std::optional VALIDATOR_LIST_THRESHOLD; + std::optional validatorListThreshold; public: Config(); @@ -305,23 +305,23 @@ public: [[nodiscard]] bool quiet() const { - return QUIET_; + return quiet_; } [[nodiscard]] bool silent() const { - return SILENT_; + return silent_; } [[nodiscard]] bool standalone() const { - return RUN_STANDALONE_; + return runStandalone_; } [[nodiscard]] bool useTxTables() const { - return USE_TX_TABLES_; + return useTxTables_; } [[nodiscard]] bool diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index d8789795c0..6eedc43edd 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -272,11 +272,11 @@ Config::Config() void Config::setupControl(bool bQuiet, bool bSilent, bool bStandalone) { - XRPL_ASSERT(NODE_SIZE == 0, "xrpl::Config::setupControl : node size not set"); + XRPL_ASSERT(nodeSize == 0, "xrpl::Config::setupControl : node size not set"); - QUIET_ = bQuiet || bSilent; - SILENT_ = bSilent; - RUN_STANDALONE_ = bStandalone; + quiet_ = bQuiet || bSilent; + silent_ = bSilent; + runStandalone_ = bStandalone; // We try to autodetect the appropriate node size by checking available // RAM and CPU resources. We default to "tiny" for standalone mode. @@ -293,15 +293,15 @@ Config::setupControl(bool bQuiet, bool bSilent, bool bStandalone) XRPL_ASSERT(ns != threshold.second.end(), "xrpl::Config::setupControl : valid node size"); if (ns != threshold.second.end()) - NODE_SIZE = std::distance(threshold.second.begin(), ns); + nodeSize = std::distance(threshold.second.begin(), ns); // Adjust the size based on the number of hardware threads of // execution available to us: if (auto const hc = std::thread::hardware_concurrency(); hc != 0) - NODE_SIZE = std::min(hc / 2, NODE_SIZE); + nodeSize = std::min(hc / 2, nodeSize); } - XRPL_ASSERT(NODE_SIZE <= 4, "xrpl::Config::setupControl : node size is set"); + XRPL_ASSERT(nodeSize <= 4, "xrpl::Config::setupControl : node size is set"); } void @@ -319,10 +319,10 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand if (!strConf.empty()) { // --conf= : everything is relative that file. - CONFIG_FILE_ = strConf; - CONFIG_DIR = boost::filesystem::absolute(CONFIG_FILE_); - CONFIG_DIR.remove_filename(); - dataDir = CONFIG_DIR / kDatabaseDirName; + configFile_ = strConf; + configDir = boost::filesystem::absolute(configFile_); + configDir.remove_filename(); + dataDir = configDir / kDatabaseDirName; } else { @@ -331,13 +331,13 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand // Check if either of the config files exist in the current working // directory, in which case the databases will be stored in a // subdirectory. - CONFIG_DIR = boost::filesystem::current_path(); - dataDir = CONFIG_DIR / kDatabaseDirName; - CONFIG_FILE_ = CONFIG_DIR / kConfigFileName; - if (boost::filesystem::exists(CONFIG_FILE_)) + configDir = boost::filesystem::current_path(); + dataDir = configDir / kDatabaseDirName; + configFile_ = configDir / kConfigFileName; + if (boost::filesystem::exists(configFile_)) break; - CONFIG_FILE_ = CONFIG_DIR / kConfigLegacyName; - if (boost::filesystem::exists(CONFIG_FILE_)) + configFile_ = configDir / kConfigLegacyName; + if (boost::filesystem::exists(configFile_)) break; // Check if the home directory is set, and optionally the XDG config @@ -362,22 +362,22 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand // Check if either of the config files exist in the XDG config // dir. dataDir = strXdgDataHome + "/" + systemName(); - CONFIG_DIR = strXdgConfigHome + "/" + systemName(); - CONFIG_FILE_ = CONFIG_DIR / kConfigFileName; - if (boost::filesystem::exists(CONFIG_FILE_)) + configDir = strXdgConfigHome + "/" + systemName(); + configFile_ = configDir / kConfigFileName; + if (boost::filesystem::exists(configFile_)) break; - CONFIG_FILE_ = CONFIG_DIR / kConfigLegacyName; - if (boost::filesystem::exists(CONFIG_FILE_)) + configFile_ = configDir / kConfigLegacyName; + if (boost::filesystem::exists(configFile_)) break; } // As a last resort, check the system config directory. dataDir = "/var/lib/" + systemName(); - CONFIG_DIR = "/etc/" + systemName(); - CONFIG_FILE_ = CONFIG_DIR / kConfigFileName; - if (boost::filesystem::exists(CONFIG_FILE_)) + configDir = "/etc/" + systemName(); + configFile_ = configDir / kConfigFileName; + if (boost::filesystem::exists(configFile_)) break; - CONFIG_FILE_ = CONFIG_DIR / kConfigLegacyName; + configFile_ = configDir / kConfigLegacyName; } while (false); } @@ -390,7 +390,7 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand { dataDir = boost::filesystem::path(dbPath); } - else if (RUN_STANDALONE_) + else if (runStandalone_) { dataDir.clear(); } @@ -407,17 +407,16 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand legacy("database_path", boost::filesystem::absolute(dataDir).string()); } - HTTPClient::initializeSSLContext( - this->SSL_VERIFY_DIR, this->SSL_VERIFY_FILE, this->SSL_VERIFY, j_); + HTTPClient::initializeSSLContext(this->sslVerifyDir, this->sslVerifyFile, this->sslVerify, j_); - if (RUN_STANDALONE_) - LEDGER_HISTORY = 0; + if (runStandalone_) + ledgerHistory = 0; Section const ledgerTxTablesSection = section("ledger_tx_tables"); - getIfExists(ledgerTxTablesSection, "use_tx_tables", USE_TX_TABLES_); + getIfExists(ledgerTxTablesSection, "use_tx_tables", useTxTables_); Section const& nodeDbSection{section(ConfigSection::nodeDatabase())}; - getIfExists(nodeDbSection, "fast_load", FAST_LOAD); + getIfExists(nodeDbSection, "fast_load", fastLoad); } // 0 ports are allowed for unit tests, but still not allowed to be present in @@ -454,16 +453,16 @@ Config::load() // NOTE: this writes to cerr because we want cout to be reserved // for the writing of the json response (so that stdout can be part of a // pipeline, for instance) - if (!QUIET_) - std::cerr << "Loading: " << CONFIG_FILE_ << "\n"; + if (!quiet_) + std::cerr << "Loading: " << configFile_ << "\n"; boost::system::error_code ec; - auto const fileContents = getFileContents(ec, CONFIG_FILE_); + auto const fileContents = getFileContents(ec, configFile_); if (ec) { - std::cerr << "Failed to read '" << CONFIG_FILE_ << "'." << ec.value() << ": " - << ec.message() << std::endl; + std::cerr << "Failed to read '" << configFile_ << "'." << ec.value() << ": " << ec.message() + << std::endl; return; } @@ -479,10 +478,10 @@ Config::loadFromString(std::string const& fileContents) build(secConfig); if (auto s = getIniFileSection(secConfig, SECTION_IPS)) - IPS = *s; + ips = *s; if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED)) - IPS_FIXED = *s; + ipsFixed = *s; // if the user has specified ip:port then replace : with a space. { @@ -502,8 +501,8 @@ Config::loadFromString(std::string const& fileContents) } }; - replaceColons(IPS_FIXED); - replaceColons(IPS); + replaceColons(ipsFixed); + replaceColons(ips); } { @@ -521,47 +520,47 @@ Config::loadFromString(std::string const& fileContents) { if (strTemp == "main") { - NETWORK_ID = 0; + networkId = 0; } else if (strTemp == "testnet") { - NETWORK_ID = 1; + networkId = 1; } else if (strTemp == "devnet") { - NETWORK_ID = 2; + networkId = 2; } else { - NETWORK_ID = beast::lexicalCastThrow(strTemp); + networkId = beast::lexicalCastThrow(strTemp); } } if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_)) - PEER_PRIVATE = beast::lexicalCastThrow(strTemp); + peerPrivate = beast::lexicalCastThrow(strTemp); if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_)) { - PEERS_MAX = beast::lexicalCastThrow(strTemp); + peersMax = beast::lexicalCastThrow(strTemp); } else { - std::optional peersInMax{}; + std::optional peersInMaxOpt{}; if (getSingleSection(secConfig, SECTION_PEERS_IN_MAX, strTemp, j_)) { - peersInMax = beast::lexicalCastThrow(strTemp); - if (*peersInMax > 1000) + peersInMaxOpt = beast::lexicalCastThrow(strTemp); + if (*peersInMaxOpt > 1000) { Throw("Invalid value specified in [" SECTION_PEERS_IN_MAX "] section; the value must be less or equal than 1000"); } } - std::optional peersOutMax{}; + std::optional peersOutMaxOpt{}; if (getSingleSection(secConfig, SECTION_PEERS_OUT_MAX, strTemp, j_)) { - peersOutMax = beast::lexicalCastThrow(strTemp); - if (*peersOutMax < 10 || *peersOutMax > 1000) + peersOutMaxOpt = beast::lexicalCastThrow(strTemp); + if (*peersOutMaxOpt < 10 || *peersOutMaxOpt > 1000) { Throw("Invalid value specified in [" SECTION_PEERS_OUT_MAX "] section; the value must be in range 10-1000"); @@ -569,17 +568,17 @@ Config::loadFromString(std::string const& fileContents) } // if one section is configured then the other must be configured too - if ((peersInMax && !peersOutMax) || (peersOutMax && !peersInMax)) + if ((peersInMaxOpt && !peersOutMaxOpt) || (peersOutMaxOpt && !peersInMaxOpt)) { Throw("Both sections [" SECTION_PEERS_IN_MAX "]" "and [" SECTION_PEERS_OUT_MAX "] must be configured"); } - if (peersInMax && peersOutMax) + if (peersInMaxOpt && peersOutMaxOpt) { - PEERS_IN_MAX = *peersInMax; - PEERS_OUT_MAX = *peersOutMax; + peersInMax = *peersInMaxOpt; + peersOutMax = *peersOutMaxOpt; } } @@ -587,27 +586,27 @@ Config::loadFromString(std::string const& fileContents) { if (boost::iequals(strTemp, "tiny")) { - NODE_SIZE = 0; + nodeSize = 0; } else if (boost::iequals(strTemp, "small")) { - NODE_SIZE = 1; + nodeSize = 1; } else if (boost::iequals(strTemp, "medium")) { - NODE_SIZE = 2; + nodeSize = 2; } else if (boost::iequals(strTemp, "large")) { - NODE_SIZE = 3; + nodeSize = 3; } else if (boost::iequals(strTemp, "huge")) { - NODE_SIZE = 4; + nodeSize = 4; } else { - NODE_SIZE = std::min(4, beast::lexicalCastThrow(strTemp)); + nodeSize = std::min(4, beast::lexicalCastThrow(strTemp)); } } @@ -615,27 +614,27 @@ Config::loadFromString(std::string const& fileContents) signingEnabled_ = beast::lexicalCastThrow(strTemp); if (getSingleSection(secConfig, SECTION_ELB_SUPPORT, strTemp, j_)) - ELB_SUPPORT = beast::lexicalCastThrow(strTemp); + elbSupport = beast::lexicalCastThrow(strTemp); - getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE, j_); - getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR, j_); + getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, sslVerifyFile, j_); + getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, sslVerifyDir, j_); if (getSingleSection(secConfig, SECTION_SSL_VERIFY, strTemp, j_)) - SSL_VERIFY = beast::lexicalCastThrow(strTemp); + sslVerify = beast::lexicalCastThrow(strTemp); if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_)) { if (boost::iequals(strTemp, "all")) { - RELAY_UNTRUSTED_VALIDATIONS = 1; + relayUntrustedValidations = 1; } else if (boost::iequals(strTemp, "trusted")) { - RELAY_UNTRUSTED_VALIDATIONS = 0; + relayUntrustedValidations = 0; } else if (boost::iequals(strTemp, "drop_untrusted")) { - RELAY_UNTRUSTED_VALIDATIONS = -1; + relayUntrustedValidations = -1; } else { @@ -648,15 +647,15 @@ Config::loadFromString(std::string const& fileContents) { if (boost::iequals(strTemp, "all")) { - RELAY_UNTRUSTED_PROPOSALS = 1; + relayUntrustedProposals = 1; } else if (boost::iequals(strTemp, "trusted")) { - RELAY_UNTRUSTED_PROPOSALS = 0; + relayUntrustedProposals = 0; } else if (boost::iequals(strTemp, "drop_untrusted")) { - RELAY_UNTRUSTED_PROPOSALS = -1; + relayUntrustedProposals = -1; } else { @@ -672,28 +671,28 @@ Config::loadFromString(std::string const& fileContents) } if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_)) - NETWORK_QUORUM = beast::lexicalCastThrow(strTemp); + networkQuorum = beast::lexicalCastThrow(strTemp); - FEES = setupFeeVote(section("voting")); + fees = setupFeeVote(section("voting")); /* [fee_default] is documented in the example config files as useful for * things like offline transaction signing. Until that's completely * deprecated, allow it to override the [voting] section. */ if (getSingleSection(secConfig, SECTION_FEE_DEFAULT, strTemp, j_)) - FEES.reference_fee = beast::lexicalCastThrow(strTemp); + fees.referenceFee = beast::lexicalCastThrow(strTemp); if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_)) { if (boost::iequals(strTemp, "full")) { - LEDGER_HISTORY = std::numeric_limits::max(); + ledgerHistory = std::numeric_limits::max(); } else if (boost::iequals(strTemp, "none")) { - LEDGER_HISTORY = 0; + ledgerHistory = 0; } else { - LEDGER_HISTORY = beast::lexicalCastThrow(strTemp); + ledgerHistory = beast::lexicalCastThrow(strTemp); } } @@ -701,42 +700,42 @@ Config::loadFromString(std::string const& fileContents) { if (boost::iequals(strTemp, "none")) { - FETCH_DEPTH = 0; + fetchDepth = 0; } else if (boost::iequals(strTemp, "full")) { - FETCH_DEPTH = std::numeric_limits::max(); + fetchDepth = std::numeric_limits::max(); } else { - FETCH_DEPTH = beast::lexicalCastThrow(strTemp); + fetchDepth = beast::lexicalCastThrow(strTemp); } - FETCH_DEPTH = std::max(FETCH_DEPTH, 10); + fetchDepth = std::max(fetchDepth, 10); } // By default, validators don't have pathfinding enabled, unless it is // explicitly requested by the server's admin. if (exists(SECTION_VALIDATION_SEED) || exists(SECTION_VALIDATOR_TOKEN)) - PATH_SEARCH_MAX = 0; + pathSearchMax = 0; if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_)) - PATH_SEARCH_OLD = beast::lexicalCastThrow(strTemp); + pathSearchOld = beast::lexicalCastThrow(strTemp); if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_)) - PATH_SEARCH = beast::lexicalCastThrow(strTemp); + pathSearch = beast::lexicalCastThrow(strTemp); if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_)) - PATH_SEARCH_FAST = beast::lexicalCastThrow(strTemp); + pathSearchFast = beast::lexicalCastThrow(strTemp); if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_)) - PATH_SEARCH_MAX = beast::lexicalCastThrow(strTemp); + pathSearchMax = beast::lexicalCastThrow(strTemp); if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_)) - DEBUG_LOGFILE_ = strTemp; + debugLogfile_ = strTemp; if (getSingleSection(secConfig, SECTION_SWEEP_INTERVAL, strTemp, j_)) { - SWEEP_INTERVAL = beast::lexicalCastThrow(strTemp); + sweepInterval = beast::lexicalCastThrow(strTemp); - if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600) + if (sweepInterval < 10 || sweepInterval > 600) { Throw("Invalid " SECTION_SWEEP_INTERVAL ": must be between 10 and 600 inclusive"); @@ -745,9 +744,9 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_)) { - WORKERS = beast::lexicalCastThrow(strTemp); + workers = beast::lexicalCastThrow(strTemp); - if (WORKERS < 1 || WORKERS > 1024) + if (workers < 1 || workers > 1024) { Throw("Invalid " SECTION_WORKERS ": must be between 1 and 1024 inclusive."); @@ -756,9 +755,9 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_IO_WORKERS, strTemp, j_)) { - IO_WORKERS = beast::lexicalCastThrow(strTemp); + ioWorkers = beast::lexicalCastThrow(strTemp); - if (IO_WORKERS < 1 || IO_WORKERS > 1024) + if (ioWorkers < 1 || ioWorkers > 1024) { Throw("Invalid " SECTION_IO_WORKERS ": must be between 1 and 1024 inclusive."); @@ -767,9 +766,9 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_PREFETCH_WORKERS, strTemp, j_)) { - PREFETCH_WORKERS = beast::lexicalCastThrow(strTemp); + prefetchWorkers = beast::lexicalCastThrow(strTemp); - if (PREFETCH_WORKERS < 1 || PREFETCH_WORKERS > 1024) + if (prefetchWorkers < 1 || prefetchWorkers > 1024) { Throw("Invalid " SECTION_PREFETCH_WORKERS ": must be between 1 and 1024 inclusive."); @@ -777,10 +776,10 @@ Config::loadFromString(std::string const& fileContents) } if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_)) - COMPRESSION = beast::lexicalCastThrow(strTemp); + compression = beast::lexicalCastThrow(strTemp); if (getSingleSection(secConfig, SECTION_LEDGER_REPLAY, strTemp, j_)) - LEDGER_REPLAY = beast::lexicalCastThrow(strTemp); + ledgerReplay = beast::lexicalCastThrow(strTemp); if (exists(SECTION_REDUCE_RELAY)) { @@ -803,15 +802,15 @@ Config::loadFromString(std::string const& fileContents) if (sec.exists("vp_base_squelch_enable")) { - VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = sec.valueOr("vp_base_squelch_enable", false); + vpReduceRelayBaseSquelchEnable = sec.valueOr("vp_base_squelch_enable", false); } else if (sec.exists("vp_enable")) { - VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = sec.valueOr("vp_enable", false); + vpReduceRelayBaseSquelchEnable = sec.valueOr("vp_enable", false); } else { - VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = false; + vpReduceRelayBaseSquelchEnable = false; } ///////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// @@ -819,9 +818,8 @@ Config::loadFromString(std::string const& fileContents) // 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. // - VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS = - sec.valueOr("vp_base_squelch_max_selected_peers", 5); - if (VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS < 3) + vpReduceRelaySquelchMaxSelectedPeers = sec.valueOr("vp_base_squelch_max_selected_peers", 5); + if (vpReduceRelaySquelchMaxSelectedPeers < 3) { Throw("Invalid " SECTION_REDUCE_RELAY " vp_base_squelch_max_selected_peers must be " @@ -829,11 +827,11 @@ Config::loadFromString(std::string const& fileContents) } ///////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// - TX_REDUCE_RELAY_ENABLE = sec.valueOr("tx_enable", false); - TX_REDUCE_RELAY_METRICS = sec.valueOr("tx_metrics", false); - TX_REDUCE_RELAY_MIN_PEERS = sec.valueOr("tx_min_peers", 20); - TX_RELAY_PERCENTAGE = sec.valueOr("tx_relay_percentage", 25); - if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 || TX_REDUCE_RELAY_MIN_PEERS < 10) + txReduceRelayEnable = sec.valueOr("tx_enable", false); + txReduceRelayMetrics = sec.valueOr("tx_metrics", false); + txReduceRelayMinPeers = sec.valueOr("tx_min_peers", 20); + txRelayPercentage = sec.valueOr("tx_relay_percentage", 25); + if (txRelayPercentage < 10 || txRelayPercentage > 100 || txReduceRelayMinPeers < 10) { Throw("Invalid " SECTION_REDUCE_RELAY ", tx_min_peers must be greater than or equal to 10" @@ -844,7 +842,7 @@ Config::loadFromString(std::string const& fileContents) if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_)) { - MAX_TRANSACTIONS = + maxTransactions = std::clamp(beast::lexicalCastThrow(strTemp), kMinJobQueueTx, kMaxJobQueueTx); } @@ -857,7 +855,7 @@ Config::loadFromString(std::string const& fileContents) ": the domain name does not appear to meet the requirements."); } - SERVER_DOMAIN = strTemp; + serverDomain = strTemp; } if (exists(SECTION_OVERLAY)) @@ -869,7 +867,7 @@ Config::loadFromString(std::string const& fileContents) try { if (auto val = sec.get("max_unknown_time")) - MAX_UNKNOWN_TIME = seconds{beast::lexicalCastThrow(*val)}; + maxUnknownTime = seconds{beast::lexicalCastThrow(*val)}; } catch (...) { @@ -877,7 +875,7 @@ Config::loadFromString(std::string const& fileContents) ": must be of the form '' representing seconds."); } - if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800}) + if (maxUnknownTime < seconds{300} || maxUnknownTime > seconds{1800}) { Throw( "Invalid value 'max_unknown_time' in " SECTION_OVERLAY @@ -887,7 +885,7 @@ Config::loadFromString(std::string const& fileContents) try { if (auto val = sec.get("max_diverged_time")) - MAX_DIVERGED_TIME = seconds{beast::lexicalCastThrow(*val)}; + maxDivergedTime = seconds{beast::lexicalCastThrow(*val)}; } catch (...) { @@ -895,7 +893,7 @@ Config::loadFromString(std::string const& fileContents) ": must be of the form '' representing seconds."); } - if (MAX_DIVERGED_TIME < seconds{60} || MAX_DIVERGED_TIME > seconds{900}) + if (maxDivergedTime < seconds{60} || maxDivergedTime > seconds{900}) { Throw("Invalid value 'max_diverged_time' in " SECTION_OVERLAY ": the time must be between 60 and 900 seconds, inclusive."); @@ -917,22 +915,22 @@ Config::loadFromString(std::string const& fileContents) if (boost::iequals(match[2], "minutes")) { - AMENDMENT_MAJORITY_TIME = minutes(duration); + amendmentMajorityTime = minutes(duration); } else if (boost::iequals(match[2], "hours")) { - AMENDMENT_MAJORITY_TIME = hours(duration); + amendmentMajorityTime = hours(duration); } else if (boost::iequals(match[2], "days")) { - AMENDMENT_MAJORITY_TIME = days(duration); + amendmentMajorityTime = days(duration); } else if (boost::iequals(match[2], "weeks")) { - AMENDMENT_MAJORITY_TIME = weeks(duration); + amendmentMajorityTime = weeks(duration); } - if (AMENDMENT_MAJORITY_TIME < minutes(15)) + if (amendmentMajorityTime < minutes(15)) { Throw("Invalid " SECTION_AMENDMENT_MAJORITY_TIME ", the minimum amount of time an amendment must hold a " @@ -941,10 +939,10 @@ Config::loadFromString(std::string const& fileContents) } if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_)) - BETA_RPC_API = beast::lexicalCastThrow(strTemp); + betaRpcApi = beast::lexicalCastThrow(strTemp); // Do not load trusted validator configuration for standalone mode - if (!RUN_STANDALONE_) + if (!runStandalone_) { // If a file was explicitly specified, then throw if the // path is malformed or if the file does not exist or is @@ -966,8 +964,8 @@ Config::loadFromString(std::string const& fileContents) "]"); } - if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty()) - validatorsFile = CONFIG_DIR / validatorsFile; + if (!validatorsFile.is_absolute() && !configDir.empty()) + validatorsFile = configDir / validatorsFile; if (!boost::filesystem::exists(validatorsFile)) { @@ -986,9 +984,9 @@ Config::loadFromString(std::string const& fileContents) validatorsFile.string()); } } - else if (!CONFIG_DIR.empty()) + else if (!configDir.empty()) { - validatorsFile = CONFIG_DIR / kValidatorsFileName; + validatorsFile = configDir / kValidatorsFileName; if (!validatorsFile.empty()) { @@ -1061,7 +1059,7 @@ Config::loadFromString(std::string const& fileContents) } } - VALIDATOR_LIST_THRESHOLD = [&]() -> std::optional { + validatorListThreshold = [&]() -> std::optional { auto const& listThreshold = section(SECTION_VALIDATOR_LIST_THRESHOLD); if (listThreshold.lines().empty()) { @@ -1119,14 +1117,14 @@ Config::loadFromString(std::string const& fileContents) // This doesn't properly belong here, but check to make sure that the // value specified for network_quorum is achievable: { - auto pm = PEERS_MAX; + auto pm = peersMax; // FIXME this apparently magic value is actually defined as a constant // elsewhere (see defaultMaxPeers) but we handle this check here. if (pm == 0) pm = 21; - if (NETWORK_QUORUM > pm) + if (networkQuorum > pm) { Throw( "The minimum number of required peers (network_quorum) exceeds " @@ -1138,13 +1136,13 @@ Config::loadFromString(std::string const& fileContents) boost::filesystem::path Config::getDebugLogFile() const { - auto logFile = DEBUG_LOGFILE_; + auto logFile = debugLogfile_; if (!logFile.empty() && !logFile.is_absolute()) { // Unless an absolute path for the log file is specified, the // path is relative to the config file directory. - logFile = boost::filesystem::absolute(logFile, CONFIG_DIR); + logFile = boost::filesystem::absolute(logFile, configDir); } if (!logFile.empty()) @@ -1175,7 +1173,7 @@ Config::getValueFor(SizedItem item, std::optional node) const auto const index = static_cast>(item); XRPL_ASSERT(index < kSizedItems.size(), "xrpl::Config::getValueFor : valid index input"); XRPL_ASSERT(!node || *node <= 4, "xrpl::Config::getValueFor : unset or valid node"); - return kSizedItems.at(index).second.at(node.value_or(NODE_SIZE)); + return kSizedItems.at(index).second.at(node.value_or(nodeSize)); } FeeSetup @@ -1186,14 +1184,14 @@ setupFeeVote(Section const& section) std::uint64_t temp = 0; if (set(temp, "reference_fee", section) && temp <= std::numeric_limits::max()) - setup.reference_fee = temp; + setup.referenceFee = temp; } { std::uint32_t temp = 0; if (set(temp, "account_reserve", section)) - setup.account_reserve = temp; + setup.accountReserve = temp; if (set(temp, "owner_reserve", section)) - setup.owner_reserve = temp; + setup.ownerReserve = temp; } return setup; } @@ -1203,7 +1201,7 @@ setupDatabaseCon(Config const& c, std::optional j) { DatabaseCon::Setup setup; - setup.startUp = c.START_UP; + setup.startUp = c.startUp; setup.standAlone = c.standalone(); setup.dataDir = c.legacy("database_path"); if (!setup.standAlone && setup.dataDir.empty()) @@ -1307,7 +1305,7 @@ setupDatabaseCon(Config const& c, std::optional j) } } - if (showRiskWarning && j && c.LEDGER_HISTORY > kSqliteTuningCutoff) + if (showRiskWarning && j && c.ledgerHistory > kSqliteTuningCutoff) { JLOG(j->warn()) << "reducing the data integrity guarantees from the " "default [sqlite] behavior is not recommended for " diff --git a/src/xrpld/overlay/Message.h b/src/xrpld/overlay/Message.h index 30d4b01cda..cd21ca40c6 100644 --- a/src/xrpld/overlay/Message.h +++ b/src/xrpld/overlay/Message.h @@ -80,7 +80,7 @@ private: std::vector buffer_; std::vector bufferCompressed_; std::size_t category_; - std::once_flag once_flag_; + std::once_flag onceFlag_; std::optional validatorKey_; /** Set the payload header diff --git a/src/xrpld/overlay/Slot.h b/src/xrpld/overlay/Slot.h index 12fecbbf42..0600265500 100644 --- a/src/xrpld/overlay/Slot.h +++ b/src/xrpld/overlay/Slot.h @@ -544,8 +544,8 @@ public: : handler_(handler) , logs_(registry.getLogs()) , journal_(registry.getJournal("Slots")) - , baseSquelchEnabled_(config.VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE) - , maxSelectedPeers_(config.VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS) + , baseSquelchEnabled_(config.vpReduceRelayBaseSquelchEnable) + , maxSelectedPeers_(config.vpReduceRelaySquelchMaxSelectedPeers) { } ~Slots() = default; diff --git a/src/xrpld/overlay/detail/ConnectAttempt.cpp b/src/xrpld/overlay/detail/ConnectAttempt.cpp index ad58cc0d2c..1167e19ca3 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.cpp +++ b/src/xrpld/overlay/detail/ConnectAttempt.cpp @@ -432,10 +432,10 @@ ConnectAttempt::onHandshake(error_code ec) req_ = makeRequest( !overlay_.peerFinder().config().peerPrivate, - app_.config().COMPRESSION, - app_.config().LEDGER_REPLAY, - app_.config().TX_REDUCE_RELAY_ENABLE, - app_.config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE); + app_.config().compression, + app_.config().ledgerReplay, + app_.config().txReduceRelayEnable, + app_.config().vpReduceRelayBaseSquelchEnable); buildHandshake( req_, diff --git a/src/xrpld/overlay/detail/Handshake.cpp b/src/xrpld/overlay/detail/Handshake.cpp index 3465dbb816..39fd93f1d4 100644 --- a/src/xrpld/overlay/detail/Handshake.cpp +++ b/src/xrpld/overlay/detail/Handshake.cpp @@ -209,8 +209,8 @@ buildHandshake( h.insert("Instance-Cookie", std::to_string(app.instanceID())); - if (!app.config().SERVER_DOMAIN.empty()) - h.insert("Server-Domain", app.config().SERVER_DOMAIN); + if (!app.config().serverDomain.empty()) + h.insert("Server-Domain", app.config().serverDomain); if (beast::IP::isPublic(remoteIp)) h.insert("Remote-IP", remoteIp.to_string()); @@ -408,10 +408,10 @@ makeResponse( "X-Protocol-Ctl", makeFeaturesResponseHeader( req, - app.config().COMPRESSION, - app.config().LEDGER_REPLAY, - app.config().TX_REDUCE_RELAY_ENABLE, - app.config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE)); + app.config().compression, + app.config().ledgerReplay, + app.config().txReduceRelayEnable, + app.config().vpReduceRelayBaseSquelchEnable)); buildHandshake(resp, sharedValue, networkID, publicIp, remoteIp, app); diff --git a/src/xrpld/overlay/detail/Message.cpp b/src/xrpld/overlay/detail/Message.cpp index 68629ce9a4..120b34c78c 100644 --- a/src/xrpld/overlay/detail/Message.cpp +++ b/src/xrpld/overlay/detail/Message.cpp @@ -203,7 +203,7 @@ Message::getBuffer(Compressed tryCompressed) if (tryCompressed == Compressed::Off) return buffer_; - std::call_once(once_flag_, &Message::compress, this); + std::call_once(onceFlag_, &Message::compress, this); if (!bufferCompressed_.empty()) { diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index a39002bd51..b31f54058a 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -112,7 +112,7 @@ OverlayImpl::Child::~Child() //------------------------------------------------------------------------------ -OverlayImpl::Timer::Timer(OverlayImpl& overlay) : Child(overlay), timer(overlay_.io_context_) +OverlayImpl::Timer::Timer(OverlayImpl& overlay) : Child(overlay), timer(overlay_.ioContext_) { } @@ -150,10 +150,10 @@ OverlayImpl::Timer::onTimer(error_code ec) overlay_.peerFinder_->oncePerSecond(); overlay_.sendEndpoints(); overlay_.autoConnect(); - if (overlay_.app_.config().TX_REDUCE_RELAY_ENABLE) + if (overlay_.app_.config().txReduceRelayEnable) overlay_.sendTxQueue(); - if ((++overlay_.timer_count_ % Tuning::kCheckIdlePeers) == 0) + if ((++overlay_.timerCount_ % Tuning::kCheckIdlePeers) == 0) overlay_.deleteIdlePeers(); asyncWait(); @@ -171,9 +171,9 @@ OverlayImpl::OverlayImpl( BasicConfig const& config, beast::insight::Collector::ptr const& collector) : app_(app) - , io_context_(ioContext) - , work_(std::in_place, boost::asio::make_work_guard(io_context_)) - , strand_(boost::asio::make_strand(io_context_)) + , ioContext_(ioContext) + , work_(std::in_place, boost::asio::make_work_guard(ioContext_)) + , strand_(boost::asio::make_strand(ioContext_)) , setup_(std::move(setup)) , journal_(app_.getJournal("Overlay")) , serverHandler_(serverHandler) @@ -186,7 +186,7 @@ OverlayImpl::OverlayImpl( config, collector)) , resolver_(resolver) - , next_id_(1) + , nextId_(1) , slots_(app, *this, app.config()) , stats_( std::bind(&OverlayImpl::collectMetrics, this), @@ -209,7 +209,7 @@ OverlayImpl::onHandoff( http_request_type&& request, endpoint_type remoteEndpoint) { - auto const id = next_id_++; + auto const id = nextId_++; auto peerJournal = app_.getJournal("Peer"); beast::WrappedSink sink(peerJournal.sink(), makePrefix(id)); beast::Journal const journal(sink); @@ -259,7 +259,7 @@ OverlayImpl::onHandoff( { handoff.moved = false; handoff.response = makeRedirectResponse(slot, request, remoteEndpoint.address()); - handoff.keep_alive = beast::rfc2616::isKeepAlive(request); + handoff.keepAlive = beast::rfc2616::isKeepAlive(request); return handoff; } } @@ -271,7 +271,7 @@ OverlayImpl::onHandoff( handoff.moved = false; handoff.response = makeErrorResponse( slot, request, remoteEndpoint.address(), "Unable to agree on a protocol version"); - handoff.keep_alive = false; + handoff.keepAlive = false; return handoff; } @@ -282,7 +282,7 @@ OverlayImpl::onHandoff( handoff.moved = false; handoff.response = makeErrorResponse(slot, request, remoteEndpoint.address(), "Incorrect security cookie"); - handoff.keep_alive = false; + handoff.keepAlive = false; return handoff; } @@ -311,7 +311,7 @@ OverlayImpl::onHandoff( << "Peer " << remoteEndpoint << " redirected, " << to_string(result); handoff.moved = false; handoff.response = makeRedirectResponse(slot, request, remoteEndpoint.address()); - handoff.keep_alive = false; + handoff.keepAlive = false; return handoff; } } @@ -351,7 +351,7 @@ OverlayImpl::onHandoff( peerFinder_->onClosed(slot); handoff.moved = false; handoff.response = makeErrorResponse(slot, request, remoteEndpoint.address(), e.what()); - handoff.keep_alive = false; + handoff.keepAlive = false; return handoff; } } @@ -444,11 +444,11 @@ OverlayImpl::connect(beast::IP::Endpoint const& remoteEndpoint) auto const p = std::make_shared( app_, - io_context_, + ioContext_, beast::IPAddressConversion::toAsioEndpoint(remoteEndpoint), usage, setup_.context, - next_id_++, + nextId_++, slot, app_.getJournal("Peer"), *this); @@ -516,7 +516,7 @@ OverlayImpl::start() // Populate our boot cache: if there are no entries in [ips] then we use // the entries in [ips_fixed]. - auto bootstrapIps = app_.config().IPS.empty() ? app_.config().IPS_FIXED : app_.config().IPS; + auto bootstrapIps = app_.config().ips.empty() ? app_.config().ipsFixed : app_.config().ips; // If nothing is specified, default to several well-known high-capacity // servers to serve as bootstrap: @@ -558,10 +558,10 @@ OverlayImpl::start() }); // Add the ips_fixed from the xrpld.cfg file - if (!app_.config().standalone() && !app_.config().IPS_FIXED.empty()) + if (!app_.config().standalone() && !app_.config().ipsFixed.empty()) { resolver_.resolve( - app_.config().IPS_FIXED, + app_.config().ipsFixed, [this](std::string const& name, std::vector const& addresses) { std::vector ips; ips.reserve(addresses.size()); @@ -1257,7 +1257,7 @@ OverlayImpl::relay( if (!relay) { - if (!app_.config().TX_REDUCE_RELAY_ENABLE) + if (!app_.config().txReduceRelayEnable) return; peers = getActivePeers(toSkip, total, disabled, enabledInSkip); @@ -1270,13 +1270,13 @@ OverlayImpl::relay( auto& txn = tx->get(); auto const sm = std::make_shared(txn, protocol::mtTRANSACTION); peers = getActivePeers(toSkip, total, disabled, enabledInSkip); - auto const minRelay = app_.config().TX_REDUCE_RELAY_MIN_PEERS + disabled; + auto const minRelay = app_.config().txReduceRelayMinPeers + disabled; - if (!app_.config().TX_REDUCE_RELAY_ENABLE || total <= minRelay) + if (!app_.config().txReduceRelayEnable || total <= minRelay) { for (auto const& p : peers) p->send(sm); - if (app_.config().TX_REDUCE_RELAY_ENABLE || app_.config().TX_REDUCE_RELAY_METRICS) + if (app_.config().txReduceRelayEnable || app_.config().txReduceRelayMetrics) txMetrics_.addMetrics(total, toSkip.size(), 0); return; } @@ -1284,8 +1284,8 @@ OverlayImpl::relay( // We have more peers than the minimum (disabled + minimum enabled), // relay to all disabled and some randomly selected enabled that // do not have the transaction. - auto const enabledTarget = app_.config().TX_REDUCE_RELAY_MIN_PEERS + - ((total - minRelay) * app_.config().TX_RELAY_PERCENTAGE / 100); + auto const enabledTarget = app_.config().txReduceRelayMinPeers + + ((total - minRelay) * app_.config().txRelayPercentage / 100); txMetrics_.addMetrics(enabledTarget, toSkip.size(), disabled); diff --git a/src/xrpld/overlay/detail/OverlayImpl.h b/src/xrpld/overlay/detail/OverlayImpl.h index 65c93a5845..6fcc2df854 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.h +++ b/src/xrpld/overlay/detail/OverlayImpl.h @@ -80,7 +80,7 @@ private: }; Application& app_; - boost::asio::io_context& io_context_; + boost::asio::io_context& ioContext_; std::optional> work_; boost::asio::strand strand_; mutable std::recursive_mutex mutex_; // VFALCO use std::mutex @@ -96,8 +96,8 @@ private: hash_map, std::weak_ptr> peers_; hash_map> ids_; Resolver& resolver_; - std::atomic next_id_; - int timer_count_{0}; + std::atomic nextId_; + int timerCount_{0}; std::atomic jqTransOverflow_{0}; std::atomic peerDisconnects_{0}; std::atomic peerDisconnectsCharges_{0}; diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 070f91c0bf..23e1785f6b 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -158,22 +158,20 @@ PeerImp::PeerImp( , request_(std::move(request)) , headers_(request_) , compressionEnabled_( - peerFeatureEnabled(headers_, kFeatureCompr, "lz4", app_.config().COMPRESSION) + peerFeatureEnabled(headers_, kFeatureCompr, "lz4", app_.config().compression) ? Compressed::On : Compressed::Off) , txReduceRelayEnabled_( - peerFeatureEnabled(headers_, kFeatureTxrr, app_.config().TX_REDUCE_RELAY_ENABLE)) + peerFeatureEnabled(headers_, kFeatureTxrr, app_.config().txReduceRelayEnable)) , ledgerReplayEnabled_( - peerFeatureEnabled(headers_, kFeatureLedgerReplay, app_.config().LEDGER_REPLAY)) + peerFeatureEnabled(headers_, kFeatureLedgerReplay, app_.config().ledgerReplay)) , ledgerReplayMsgHandler_(app, app.getLedgerReplayer()) { - JLOG(journal_.info()) << "compression enabled " << (compressionEnabled_ == Compressed::On) - << " vp reduce-relay base squelch enabled " - << peerFeatureEnabled( - headers_, - kFeatureVprr, - app_.config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE) - << " tx reduce-relay enabled " << txReduceRelayEnabled_; + JLOG(journal_.info()) + << "compression enabled " << (compressionEnabled_ == Compressed::On) + << " vp reduce-relay base squelch enabled " + << peerFeatureEnabled(headers_, kFeatureVprr, app_.config().vpReduceRelayBaseSquelchEnable) + << " tx reduce-relay enabled " << txReduceRelayEnabled_; } PeerImp::~PeerImp() @@ -817,8 +815,8 @@ PeerImp::onTimer(error_code const& ec) duration = clock_type::now() - trackingTime_; } - if ((t == Tracking::Diverged && (duration > app_.config().MAX_DIVERGED_TIME)) || - (t == Tracking::Unknown && (duration > app_.config().MAX_UNKNOWN_TIME))) + if ((t == Tracking::Diverged && (duration > app_.config().maxDivergedTime)) || + (t == Tracking::Unknown && (duration > app_.config().maxUnknownTime))) { overlay_.peerFinder().onFailure(slot_); fail("Not useful"); @@ -1208,7 +1206,7 @@ PeerImp::onMessageBegin( // LEDGER_DATA category == TrafficCount::Category::GlTscShare || category == TrafficCount::Category::GlTscGet) && - (txReduceRelayEnabled() || app_.config().TX_REDUCE_RELAY_METRICS)) + (txReduceRelayEnabled() || app_.config().txReduceRelayMetrics)) { overlay_.addTxMetrics(static_cast(type), static_cast(size)); } @@ -1518,7 +1516,7 @@ PeerImp::handleTransaction( { JLOG(pJournal_.trace()) << "No new transactions until synchronized"; } - else if (app_.getJobQueue().getJobCount(JtTransaction) > app_.config().MAX_TRANSACTIONS) + else if (app_.getJobQueue().getJobCount(JtTransaction) > app_.config().maxTransactions) { overlay_.incJqTransOverflow(); JLOG(pJournal_.info()) << "Transaction queue is full"; @@ -1889,7 +1887,7 @@ PeerImp::onMessage(std::shared_ptr const& m) overlay_.reportInboundTraffic( TrafficCount::Category::ProposalUntrusted, Message::messageSize(*m)); - if (app_.config().RELAY_UNTRUSTED_PROPOSALS == -1) + if (app_.config().relayUntrustedProposals == -1) return; } @@ -2496,7 +2494,7 @@ PeerImp::onMessage(std::shared_ptr const& m) overlay_.reportInboundTraffic( TrafficCount::Category::ValidationUntrusted, Message::messageSize(*m)); - if (app_.config().RELAY_UNTRUSTED_VALIDATIONS == -1) + if (app_.config().relayUntrustedValidations == -1) return; } @@ -3093,7 +3091,7 @@ PeerImp::checkPropose( } else { - relay = app_.config().RELAY_UNTRUSTED_PROPOSALS == 1 || cluster(); + relay = app_.config().relayUntrustedProposals == 1 || cluster(); } if (relay) diff --git a/src/xrpld/overlay/detail/PeerImp.h b/src/xrpld/overlay/detail/PeerImp.h index 9c66347810..dca70d88bd 100644 --- a/src/xrpld/overlay/detail/PeerImp.h +++ b/src/xrpld/overlay/detail/PeerImp.h @@ -839,25 +839,23 @@ PeerImp::PeerImp( , response_(std::move(response)) , headers_(response_) , compressionEnabled_( - peerFeatureEnabled(headers_, kFeatureCompr, "lz4", app_.config().COMPRESSION) + peerFeatureEnabled(headers_, kFeatureCompr, "lz4", app_.config().compression) ? Compressed::On : Compressed::Off) , txReduceRelayEnabled_( - peerFeatureEnabled(headers_, kFeatureTxrr, app_.config().TX_REDUCE_RELAY_ENABLE)) + peerFeatureEnabled(headers_, kFeatureTxrr, app_.config().txReduceRelayEnable)) , ledgerReplayEnabled_( - peerFeatureEnabled(headers_, kFeatureLedgerReplay, app_.config().LEDGER_REPLAY)) + peerFeatureEnabled(headers_, kFeatureLedgerReplay, app_.config().ledgerReplay)) , ledgerReplayMsgHandler_(app, app.getLedgerReplayer()) { readBuffer_.commit( boost::asio::buffer_copy(readBuffer_.prepare(boost::asio::buffer_size(buffers)), buffers)); - JLOG(journal_.info()) << "compression enabled " << (compressionEnabled_ == Compressed::On) - << " vp reduce-relay base squelch enabled " - << peerFeatureEnabled( - headers_, - kFeatureVprr, - app_.config().VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE) - << " tx reduce-relay enabled " << txReduceRelayEnabled_ << " on " - << remoteAddress_ << " " << id_; + JLOG(journal_.info()) + << "compression enabled " << (compressionEnabled_ == Compressed::On) + << " vp reduce-relay base squelch enabled " + << peerFeatureEnabled(headers_, kFeatureVprr, app_.config().vpReduceRelayBaseSquelchEnable) + << " tx reduce-relay enabled " << txReduceRelayEnabled_ << " on " << remoteAddress_ << " " + << id_; } template diff --git a/src/xrpld/overlay/detail/ProtocolMessage.h b/src/xrpld/overlay/detail/ProtocolMessage.h index ab41cbe337..b1a30bad10 100644 --- a/src/xrpld/overlay/detail/ProtocolMessage.h +++ b/src/xrpld/overlay/detail/ProtocolMessage.h @@ -98,19 +98,19 @@ struct MessageHeader @note This is the sum of sizes of the header and the payload. */ - std::uint32_t total_wire_size = 0; + std::uint32_t totalWireSize = 0; /** The size of the header associated with this message. */ - std::uint32_t header_size = 0; + std::uint32_t headerSize = 0; /** The size of the payload on the wire. */ - std::uint32_t payload_wire_size = 0; + std::uint32_t payloadWireSize = 0; /** Uncompressed message size if the message is compressed. */ - std::uint32_t uncompressed_size = 0; + std::uint32_t uncompressedSize = 0; /** The type of the message. */ - std::uint16_t message_type = 0; + std::uint16_t messageType = 0; /** Indicates which compression algorithm the payload is compressed with. * Currently only lz4 is supported. If None then the message is not @@ -159,10 +159,10 @@ parseMessageHeader(boost::system::error_code& ec, BufferSequence const& bufs, st // - 32 bits are the uncompressed data size if (*iter & 0x80) { - hdr.header_size = kHeaderBytesCompressed; + hdr.headerSize = kHeaderBytesCompressed; // not enough bytes to parse the header - if (size < hdr.header_size) + if (size < hdr.headerSize) { ec = make_error_code(boost::system::errc::success); return std::nullopt; @@ -183,18 +183,18 @@ parseMessageHeader(boost::system::error_code& ec, BufferSequence const& bufs, st } for (int i = 0; i != 4; ++i) - hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++; + hdr.payloadWireSize = (hdr.payloadWireSize << 8) + *iter++; // clear the top four bits (the compression bits). - hdr.payload_wire_size &= 0x0FFFFFFF; + hdr.payloadWireSize &= 0x0FFFFFFF; - hdr.total_wire_size = hdr.header_size + hdr.payload_wire_size; + hdr.totalWireSize = hdr.headerSize + hdr.payloadWireSize; for (int i = 0; i != 2; ++i) - hdr.message_type = (hdr.message_type << 8) + *iter++; + hdr.messageType = (hdr.messageType << 8) + *iter++; for (int i = 0; i != 4; ++i) - hdr.uncompressed_size = (hdr.uncompressed_size << 8) + *iter++; + hdr.uncompressedSize = (hdr.uncompressedSize << 8) + *iter++; return hdr; } @@ -204,9 +204,9 @@ parseMessageHeader(boost::system::error_code& ec, BufferSequence const& bufs, st // - 26 bits are the payload size if ((*iter & 0xFC) == 0) { - hdr.header_size = kHeaderBytes; + hdr.headerSize = kHeaderBytes; - if (size < hdr.header_size) + if (size < hdr.headerSize) { ec = make_error_code(boost::system::errc::success); return std::nullopt; @@ -215,13 +215,13 @@ parseMessageHeader(boost::system::error_code& ec, BufferSequence const& bufs, st hdr.algorithm = Algorithm::None; for (int i = 0; i != 4; ++i) - hdr.payload_wire_size = (hdr.payload_wire_size << 8) + *iter++; + hdr.payloadWireSize = (hdr.payloadWireSize << 8) + *iter++; - hdr.uncompressed_size = hdr.payload_wire_size; - hdr.total_wire_size = hdr.header_size + hdr.payload_wire_size; + hdr.uncompressedSize = hdr.payloadWireSize; + hdr.totalWireSize = hdr.headerSize + hdr.payloadWireSize; for (int i = 0; i != 2; ++i) - hdr.message_type = (hdr.message_type << 8) + *iter++; + hdr.messageType = (hdr.messageType << 8) + *iter++; return hdr; } @@ -240,18 +240,18 @@ parseMessageContent(MessageHeader const& header, Buffers const& buffers) auto m = std::make_shared(); ZeroCopyInputStream stream(buffers); - stream.Skip(header.header_size); + stream.Skip(header.headerSize); if (header.algorithm != compression::Algorithm::None) { std::vector payload; - payload.resize(header.uncompressed_size); + payload.resize(header.uncompressedSize); auto const payloadSize = xrpl::compression::decompress( stream, - header.payload_wire_size, + header.payloadWireSize, payload.data(), - header.uncompressed_size, + header.uncompressedSize, header.algorithm); if (payloadSize == 0 || !m->ParseFromArray(payload.data(), payloadSize)) @@ -279,13 +279,13 @@ invoke(MessageHeader const& header, Buffers const& buffers, Handler& handler) using namespace xrpl::compression; handler.onMessageBegin( - header.message_type, + header.messageType, m, - header.payload_wire_size, - header.uncompressed_size, + header.payloadWireSize, + header.uncompressedSize, header.algorithm != Algorithm::None); handler.onMessage(m); - handler.onMessageEnd(header.message_type, m); + handler.onMessageEnd(header.messageType, m); return true; } @@ -329,8 +329,8 @@ invokeProtocolMessage(Buffers const& buffers, Handler& handler, std::size_t& hin // whose size exceeds this may result in the connection being dropped. A // larger message size may be supported in the future or negotiated as // part of a protocol upgrade. - if (header->payload_wire_size > kMaximumMessageSize || - header->uncompressed_size > kMaximumMessageSize) + if (header->payloadWireSize > kMaximumMessageSize || + header->uncompressedSize > kMaximumMessageSize) { result.second = make_error_code(boost::system::errc::message_size); return result; @@ -345,15 +345,15 @@ invokeProtocolMessage(Buffers const& buffers, Handler& handler, std::size_t& hin // We don't have the whole message yet. This isn't an error but we have // nothing to do. - if (header->total_wire_size > size) + if (header->totalWireSize > size) { - hint = header->total_wire_size - size; + hint = header->totalWireSize - size; return result; } bool success = false; - switch (header->message_type) + switch (header->messageType) { case protocol::mtMANIFESTS: success = detail::invoke(*header, buffers, handler); @@ -420,12 +420,12 @@ invokeProtocolMessage(Buffers const& buffers, Handler& handler, std::size_t& hin success = detail::invoke(*header, buffers, handler); break; default: - handler.onMessageUnknown(header->message_type); + handler.onMessageUnknown(header->messageType); success = true; break; } - result.first = header->total_wire_size; + result.first = header->totalWireSize; if (!success) result.second = make_error_code(boost::system::errc::bad_message); diff --git a/src/xrpld/overlay/detail/TxMetrics.cpp b/src/xrpld/overlay/detail/TxMetrics.cpp index 2df072ef0c..d2597cb19f 100644 --- a/src/xrpld/overlay/detail/TxMetrics.cpp +++ b/src/xrpld/overlay/detail/TxMetrics.cpp @@ -77,13 +77,13 @@ SingleMetrics::addMetrics(std::uint32_t val) { using namespace std::chrono_literals; accum += val; - N++; + n++; auto const timeElapsed = clock_type::now() - intervalStart; auto const timeElapsedInSecs = std::chrono::duration_cast(timeElapsed); if (timeElapsedInSecs >= 1s) { - auto const avg = accum / (perTimeUnit ? timeElapsedInSecs.count() : N); + auto const avg = accum / (perTimeUnit ? timeElapsedInSecs.count() : n); rollingAvgAggregate.push_back(avg); auto const total = @@ -92,7 +92,7 @@ SingleMetrics::addMetrics(std::uint32_t val) intervalStart = clock_type::now(); accum = 0; - N = 0; + n = 0; } } diff --git a/src/xrpld/overlay/detail/TxMetrics.h b/src/xrpld/overlay/detail/TxMetrics.h index 7b8b4bae1b..50df7a65cc 100644 --- a/src/xrpld/overlay/detail/TxMetrics.h +++ b/src/xrpld/overlay/detail/TxMetrics.h @@ -29,7 +29,7 @@ struct SingleMetrics clock_type::time_point intervalStart{clock_type::now()}; std::uint64_t accum{0}; std::uint64_t rollingAvg{0}; - std::uint32_t N{0}; + std::uint32_t n{0}; bool perTimeUnit{true}; boost::circular_buffer rollingAvgAggregate{30, 0ull}; /** Add metrics value diff --git a/src/xrpld/peerfinder/detail/Counts.h b/src/xrpld/peerfinder/detail/Counts.h index ff88421e09..67b8370996 100644 --- a/src/xrpld/peerfinder/detail/Counts.h +++ b/src/xrpld/peerfinder/detail/Counts.h @@ -42,9 +42,9 @@ public: return true; if (s.inbound()) - return in_active_ < in_max_; + return inActive_ < inMax_; - return out_active_ < out_max_; + return outActive_ < outMax_; } /** Returns the number of attempts needed to bring us to the max. */ @@ -67,7 +67,7 @@ public: [[nodiscard]] int outMax() const { - return out_max_; + return outMax_; } /** Returns the number of outbound peers assigned an open slot. @@ -76,7 +76,7 @@ public: [[nodiscard]] int outActive() const { - return out_active_; + return outActive_; } /** Returns the number of fixed connections. */ @@ -90,7 +90,7 @@ public: [[nodiscard]] std::size_t fixedActive() const { - return fixed_active_; + return fixedActive_; } //-------------------------------------------------------------------------- @@ -99,9 +99,9 @@ public: void onConfig(Config const& config) { - out_max_ = config.outPeers; + outMax_ = config.outPeers; if (config.wantIncoming) - in_max_ = config.inPeers; + inMax_ = config.inPeers; } /** Returns the number of accepted connections that haven't handshaked. */ @@ -129,21 +129,21 @@ public: [[nodiscard]] int inMax() const { - return in_max_; + return inMax_; } /** Returns the number of inbound peers assigned an open slot. */ [[nodiscard]] int inboundActive() const { - return in_active_; + return inActive_; } /** Returns the total number of active peers excluding fixed peers. */ [[nodiscard]] int totalActive() const { - return in_active_ + out_active_; + return inActive_ + outActive_; } /** Returns the number of unused inbound slots. @@ -152,8 +152,8 @@ public: [[nodiscard]] int inboundSlotsFree() const { - if (in_active_ < in_max_) - return in_max_ - in_active_; + if (inActive_ < inMax_) + return inMax_ - inActive_; return 0; } @@ -163,8 +163,8 @@ public: [[nodiscard]] int outboundSlotsFree() const { - if (out_active_ < out_max_) - return out_max_ - out_active_; + if (outActive_ < outMax_) + return outMax_ - outActive_; return 0; } @@ -181,7 +181,7 @@ public: // // Fixed peers do not count towards the active outgoing total. - return out_max_ <= 0; + return outMax_ <= 0; } /** Output statistics. */ @@ -191,9 +191,9 @@ public: map["accept"] = acceptCount(); map["connect"] = connectCount(); map["close"] = closingCount(); - map["in"] << in_active_ << "/" << in_max_; - map["out"] << out_active_ << "/" << out_max_; - map["fixed"] = fixed_active_; + map["in"] << inActive_ << "/" << inMax_; + map["out"] << outActive_ << "/" << outMax_; + map["fixed"] = fixedActive_; map["reserved"] = reserved_; map["total"] = active_; } @@ -203,7 +203,7 @@ public: stateString() const { std::stringstream ss; - ss << out_active_ << "/" << out_max_ << " out, " << in_active_ << "/" << in_max_ << " in, " + ss << outActive_ << "/" << outMax_ << " out, " << inActive_ << "/" << inMax_ << " in, " << connectCount() << " connecting, " << closingCount() << " closing"; return ss.str(); } @@ -262,16 +262,16 @@ private: case Slot::State::Active: if (s.fixed()) - adjustCounter(fixed_active_, dir); + adjustCounter(fixedActive_, dir); if (!s.fixed() && !s.reserved()) { if (s.inbound()) { - adjustCounter(in_active_, dir); + adjustCounter(inActive_, dir); } else { - adjustCounter(out_active_, dir); + adjustCounter(outActive_, dir); } } adjustCounter(active_, dir); @@ -297,22 +297,22 @@ private: std::size_t active_{0}; /** Total number of inbound slots. */ - std::size_t in_max_{0}; + std::size_t inMax_{0}; /** Number of inbound slots assigned to active peers. */ - std::size_t in_active_{0}; + std::size_t inActive_{0}; /** Maximum desired outbound slots. */ - std::size_t out_max_{0}; + std::size_t outMax_{0}; /** Active outbound slots. */ - std::size_t out_active_{0}; + std::size_t outActive_{0}; /** Fixed connections. */ std::size_t fixed_{0}; /** Active fixed connections. */ - std::size_t fixed_active_{0}; + std::size_t fixedActive_{0}; /** Reserved connections. */ std::size_t reserved_{0}; diff --git a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp index bbaeeff8e6..fcf30fa4f4 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp @@ -63,15 +63,15 @@ Config::makeConfig( { PeerFinder::Config config; - config.peerPrivate = cfg.PEER_PRIVATE; + config.peerPrivate = cfg.peerPrivate; // Servers with peer privacy don't want to allow incoming connections config.wantIncoming = (!config.peerPrivate) && (port != 0); - if ((cfg.PEERS_OUT_MAX == 0u) && (cfg.PEERS_IN_MAX == 0u)) + if ((cfg.peersOutMax == 0u) && (cfg.peersInMax == 0u)) { - if (cfg.PEERS_MAX != 0) - config.maxPeers = cfg.PEERS_MAX; + if (cfg.peersMax != 0) + config.maxPeers = cfg.peersMax; config.maxPeers = std::max(config.maxPeers, Tuning::kMinOutCount); config.outPeers = config.calcOutPeers(); @@ -94,8 +94,8 @@ Config::makeConfig( } else { - config.outPeers = cfg.PEERS_OUT_MAX; - config.inPeers = cfg.PEERS_IN_MAX; + config.outPeers = cfg.peersOutMax; + config.inPeers = cfg.peersInMax; config.maxPeers = 0; } @@ -108,7 +108,7 @@ Config::makeConfig( // if it's a private peer or we are running as standalone // automatic connections would defeat the purpose. - config.autoConnect = !cfg.standalone() && !cfg.PEER_PRIVATE; + config.autoConnect = !cfg.standalone() && !cfg.peerPrivate; config.listeningPort = port; config.features = ""; config.ipLimit = ipLimit; diff --git a/src/xrpld/peerfinder/detail/SlotImp.cpp b/src/xrpld/peerfinder/detail/SlotImp.cpp index f6ff24e6a6..a54ddb56e7 100644 --- a/src/xrpld/peerfinder/detail/SlotImp.cpp +++ b/src/xrpld/peerfinder/detail/SlotImp.cpp @@ -23,9 +23,9 @@ SlotImp::SlotImp( , fixed_(fixed) , reserved_(false) , state_(State::Accept) - , remote_endpoint_(std::move(remoteEndpoint)) - , local_endpoint_(localEndpoint) - , listening_port_(kUnknownPort) + , remoteEndpoint_(std::move(remoteEndpoint)) + , localEndpoint_(localEndpoint) + , listeningPort_(kUnknownPort) , checked(false) , canAccept(false) , connectivityCheckInProgress(false) @@ -38,8 +38,8 @@ SlotImp::SlotImp(beast::IP::Endpoint remoteEndpoint, bool fixed, clock_type& clo , fixed_(fixed) , reserved_(false) , state_(State::Connect) - , remote_endpoint_(std::move(remoteEndpoint)) - , listening_port_(kUnknownPort) + , remoteEndpoint_(std::move(remoteEndpoint)) + , listeningPort_(kUnknownPort) , checked(true) , canAccept(true) , connectivityCheckInProgress(false) diff --git a/src/xrpld/peerfinder/detail/SlotImp.h b/src/xrpld/peerfinder/detail/SlotImp.h index 0d09a37539..0cf4d7a3c8 100644 --- a/src/xrpld/peerfinder/detail/SlotImp.h +++ b/src/xrpld/peerfinder/detail/SlotImp.h @@ -52,19 +52,19 @@ public: beast::IP::Endpoint const& remoteEndpoint() const override { - return remote_endpoint_; + return remoteEndpoint_; } std::optional const& localEndpoint() const override { - return local_endpoint_; + return localEndpoint_; } std::optional const& publicKey() const override { - return public_key_; + return publicKey_; } std::string @@ -76,7 +76,7 @@ public: std::optional listeningPort() const override { - std::uint32_t const value = listening_port_; + std::uint32_t const value = listeningPort_; if (value == kUnknownPort) return std::nullopt; return value; @@ -85,25 +85,25 @@ public: void setListeningPort(std::uint16_t port) { - listening_port_ = port; + listeningPort_ = port; } void localEndpoint(beast::IP::Endpoint const& endpoint) { - local_endpoint_ = endpoint; + localEndpoint_ = endpoint; } void remoteEndpoint(beast::IP::Endpoint const& endpoint) { - remote_endpoint_ = endpoint; + remoteEndpoint_ = endpoint; } void publicKey(PublicKey const& key) { - public_key_ = key; + publicKey_ = key; } void @@ -160,12 +160,12 @@ private: bool const fixed_; bool reserved_; State state_; - beast::IP::Endpoint remote_endpoint_; - std::optional local_endpoint_; - std::optional public_key_; + beast::IP::Endpoint remoteEndpoint_; + std::optional localEndpoint_; + std::optional publicKey_; static constexpr std::int32_t kUnknownPort = -1; - std::atomic listening_port_; + std::atomic listeningPort_; public: // DEPRECATED public data members diff --git a/src/xrpld/rpc/ServerHandler.h b/src/xrpld/rpc/ServerHandler.h index 6b4cc34cc5..0aac05ea44 100644 --- a/src/xrpld/rpc/ServerHandler.h +++ b/src/xrpld/rpc/ServerHandler.h @@ -46,8 +46,8 @@ public: std::uint16_t port = 0; std::string user; std::string password; - std::string admin_user; - std::string admin_password; + std::string adminUser; + std::string adminPassword; }; // Configuration when acting in client role @@ -72,9 +72,9 @@ private: Setup setup_; Endpoints endpoints_; JobQueue& jobQueue_; - beast::insight::Counter rpc_requests_; - beast::insight::Event rpc_size_; - beast::insight::Event rpc_time_; + beast::insight::Counter rpcRequests_; + beast::insight::Event rpcSize_; + beast::insight::Event rpcTime_; std::mutex mutex_; std::condition_variable condition_; bool stopped_{false}; diff --git a/src/xrpld/rpc/detail/AssetCache.h b/src/xrpld/rpc/detail/AssetCache.h index 9112d78715..dd53620cdf 100644 --- a/src/xrpld/rpc/detail/AssetCache.h +++ b/src/xrpld/rpc/detail/AssetCache.h @@ -56,10 +56,10 @@ private: { AccountID account; LineDirection direction; - std::size_t hash_value; + std::size_t hashValue; AccountKey(AccountID const& account, LineDirection direction, std::size_t hash) - : account(account), direction(direction), hash_value(hash) + : account(account), direction(direction), hashValue(hash) { } @@ -71,14 +71,14 @@ private: bool operator==(AccountKey const& lhs) const { - return hash_value == lhs.hash_value && account == lhs.account && + return hashValue == lhs.hashValue && account == lhs.account && direction == lhs.direction; } [[nodiscard]] std::size_t getHash() const { - return hash_value; + return hashValue; } struct Hash diff --git a/src/xrpld/rpc/detail/PathRequest.cpp b/src/xrpld/rpc/detail/PathRequest.cpp index 6207b3393f..06507319f7 100644 --- a/src/xrpld/rpc/detail/PathRequest.cpp +++ b/src/xrpld/rpc/detail/PathRequest.cpp @@ -101,16 +101,16 @@ PathRequest::~PathRequest() return; std::string fast, full; - if (quick_reply_ != steady_clock::time_point{}) + if (quickReply_ != steady_clock::time_point{}) { fast = " fast:"; - fast += std::to_string(duration_cast(quick_reply_ - created_).count()); + fast += std::to_string(duration_cast(quickReply_ - created_).count()); fast += "ms"; } - if (full_reply_ != steady_clock::time_point{}) + if (fullReply_ != steady_clock::time_point{}) { full = " full:"; - full += std::to_string(duration_cast(full_reply_ - created_).count()); + full += std::to_string(duration_cast(fullReply_ - created_).count()); full += "ms"; } stream << iIdentifier_ << " complete:" << fast << full @@ -180,7 +180,7 @@ PathRequest::isValid(std::shared_ptr const& crCache) if (!raSrcAccount_ || !raDstAccount_) return false; - if (!convert_all_ && (saSendMax_ || saDstAmount_ <= beast::kZero)) + if (!convertAll_ && (saSendMax_ || saDstAmount_ <= beast::kZero)) { // If send max specified, dst amt must be -1. jvStatus_ = rpcError(RpcDstAmtMalformed); @@ -210,7 +210,7 @@ PathRequest::isValid(std::shared_ptr const& crCache) return false; } - if (!convert_all_ && saDstAmount_ < STAmount(lrLedger->fees().reserve)) + if (!convertAll_ && saDstAmount_ < STAmount(lrLedger->fees().reserve)) { // Payment must meet reserve. jvStatus_ = rpcError(RpcDstAmtMalformed); @@ -312,9 +312,9 @@ PathRequest::parseJson(json::Value const& jvParams) return PFR_PJ_INVALID; } - convert_all_ = saDstAmount_ == STAmount(saDstAmount_.asset(), 1u, 0, true); + convertAll_ = saDstAmount_ == STAmount(saDstAmount_.asset(), 1u, 0, true); - if (!validAsset(saDstAmount_.asset()) || (!convert_all_ && saDstAmount_ <= beast::kZero)) + if (!validAsset(saDstAmount_.asset()) || (!convertAll_ && saDstAmount_ <= beast::kZero)) { jvStatus_ = rpcError(RpcDstAmtMalformed); return PFR_PJ_INVALID; @@ -323,7 +323,7 @@ PathRequest::parseJson(json::Value const& jvParams) if (jvParams.isMember(jss::send_max)) { // Send_max requires destination amount to be -1. - if (!convert_all_) + if (!convertAll_) { jvStatus_ = rpcError(RpcDstAmtMalformed); return PFR_PJ_INVALID; @@ -577,7 +577,7 @@ PathRequest::findPaths( } } - auto const dstAmount = convertAmount(saDstAmount_, convert_all_); + auto const dstAmount = convertAmount(saDstAmount_, convertAll_); hash_map> currencyMap; for (auto const& asset : sourceAssets) { @@ -622,7 +622,7 @@ PathRequest::findPaths( JLOG(journal_.debug()) << iIdentifier_ << " Paths found, calling rippleCalc"; path::RippleCalc::Input rcInput; - if (convert_all_) + if (convertAll_) rcInput.partialPaymentAllowed = true; auto sandbox = std::make_unique(&*cache->getLedger(), TapNone); auto rc = path::RippleCalc::rippleCalculate( @@ -639,7 +639,7 @@ PathRequest::findPaths( app_, &rcInput); - if (!convert_all_ && !fullLiquidityPath.empty() && + if (!convertAll_ && !fullLiquidityPath.empty() && (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) { JLOG(journal_.debug()) << iIdentifier_ << " Trying with an extra path element"; @@ -679,7 +679,7 @@ PathRequest::findPaths( jvEntry[jss::source_amount] = rc.actualAmountIn.getJson(JsonOptions::Values::None); jvEntry[jss::paths_computed] = ps.getJson(JsonOptions::Values::None); - if (convert_all_) + if (convertAll_) { jvEntry[jss::destination_amount] = rc.actualAmountOut.getJson(JsonOptions::Values::None); @@ -754,33 +754,33 @@ PathRequest::doUpdate( // first pass if (loaded || fast) { - iLevel_ = app_.config().PATH_SEARCH_FAST; + iLevel_ = app_.config().pathSearchFast; } else { - iLevel_ = app_.config().PATH_SEARCH; + iLevel_ = app_.config().pathSearch; } } - else if ((iLevel_ == app_.config().PATH_SEARCH_FAST) && !fast) + else if ((iLevel_ == app_.config().pathSearchFast) && !fast) { // leaving fast pathfinding - iLevel_ = app_.config().PATH_SEARCH; - if (loaded && (iLevel_ > app_.config().PATH_SEARCH_FAST)) + iLevel_ = app_.config().pathSearch; + if (loaded && (iLevel_ > app_.config().pathSearchFast)) --iLevel_; } else if (bLastSuccess_) { // decrement, if possible - if (iLevel_ > app_.config().PATH_SEARCH || - (loaded && (iLevel_ > app_.config().PATH_SEARCH_FAST))) + if (iLevel_ > app_.config().pathSearch || + (loaded && (iLevel_ > app_.config().pathSearchFast))) --iLevel_; } else { // adjust as needed - if (!loaded && (iLevel_ < app_.config().PATH_SEARCH_MAX)) + if (!loaded && (iLevel_ < app_.config().pathSearchMax)) ++iLevel_; - if (loaded && (iLevel_ > app_.config().PATH_SEARCH_FAST)) + if (loaded && (iLevel_ > app_.config().pathSearchFast)) --iLevel_; } @@ -798,15 +798,15 @@ PathRequest::doUpdate( newStatus = rpcError(RpcInternal); } - if (fast && quick_reply_ == steady_clock::time_point{}) + if (fast && quickReply_ == steady_clock::time_point{}) { - quick_reply_ = steady_clock::now(); - owner_.reportFast(duration_cast(quick_reply_ - created_)); + quickReply_ = steady_clock::now(); + owner_.reportFast(duration_cast(quickReply_ - created_)); } - else if (!fast && full_reply_ == steady_clock::time_point{}) + else if (!fast && fullReply_ == steady_clock::time_point{}) { - full_reply_ = steady_clock::now(); - owner_.reportFull(duration_cast(full_reply_ - created_)); + fullReply_ = steady_clock::now(); + owner_.reportFull(duration_cast(fullReply_ - created_)); } { diff --git a/src/xrpld/rpc/detail/PathRequest.h b/src/xrpld/rpc/detail/PathRequest.h index 17381c5ea5..de8c10de0e 100644 --- a/src/xrpld/rpc/detail/PathRequest.h +++ b/src/xrpld/rpc/detail/PathRequest.h @@ -140,7 +140,7 @@ private: std::optional domain_; - bool convert_all_{}; + bool convertAll_{}; std::recursive_mutex indexLock_; LedgerIndex lastIndex_; @@ -152,8 +152,8 @@ private: int const iIdentifier_; std::chrono::steady_clock::time_point const created_; - std::chrono::steady_clock::time_point quick_reply_; - std::chrono::steady_clock::time_point full_reply_; + std::chrono::steady_clock::time_point quickReply_; + std::chrono::steady_clock::time_point fullReply_; static unsigned int const kMaxPaths = 4; }; diff --git a/src/xrpld/rpc/detail/Pathfinder.cpp b/src/xrpld/rpc/detail/Pathfinder.cpp index e949c9b6ff..daa50cfb07 100644 --- a/src/xrpld/rpc/detail/Pathfinder.cpp +++ b/src/xrpld/rpc/detail/Pathfinder.cpp @@ -226,7 +226,7 @@ Pathfinder::Pathfinder( , srcPathAsset_(uSrcPathAsset) , srcIssuer_(uSrcIssuer) , srcAmount_(amountFromPathAsset(uSrcPathAsset, uSrcIssuer, uSrcAccount)) - , convert_all_(convertAllCheck(dstAmount_)) + , convertAll_(convertAllCheck(dstAmount_)) , domain_(domain) , ledger_(cache->getLedger()) , rLCache_(cache) @@ -398,7 +398,7 @@ Pathfinder::getPathLiquidity( try { // Compute a path that provides at least the minimum liquidity. - if (convert_all_) + if (convertAll_) rcInput.partialPaymentAllowed = true; auto rc = path::RippleCalc::rippleCalculate( @@ -418,7 +418,7 @@ Pathfinder::getPathLiquidity( qualityOut = getRate(rc.actualAmountOut, rc.actualAmountIn); amountOut = rc.actualAmountOut; - if (!convert_all_) + if (!convertAll_) { // Now try to compute the remaining liquidity. rcInput.partialPaymentAllowed = true; @@ -451,7 +451,7 @@ Pathfinder::getPathLiquidity( void Pathfinder::computePathRanks(int maxPaths, std::function const& continueCallback) { - remainingAmount_ = convertAmount(dstAmount_, convert_all_); + remainingAmount_ = convertAmount(dstAmount_, convertAll_); // Must subtract liquidity in default path from remaining amount. try @@ -536,7 +536,7 @@ Pathfinder::rankPaths( rankedPaths.reserve(paths.size()); auto const saMinDstAmount = [&]() -> STAmount { - if (!convert_all_) + if (!convertAll_) { // Ignore paths that move only very small amounts. return smallestUsefulAmount(dstAmount_, maxPaths); @@ -581,7 +581,7 @@ Pathfinder::rankPaths( std::ranges::sort( rankedPaths, [&](Pathfinder::PathRank const& a, Pathfinder::PathRank const& b) { // 1) Higher quality (lower cost) is better - if (!convert_all_ && a.quality != b.quality) + if (!convertAll_ && a.quality != b.quality) return a.quality < b.quality; // 2) More liquidity (higher volume) is better diff --git a/src/xrpld/rpc/detail/Pathfinder.h b/src/xrpld/rpc/detail/Pathfinder.h index de2c2d83e6..36caab308e 100644 --- a/src/xrpld/rpc/detail/Pathfinder.h +++ b/src/xrpld/rpc/detail/Pathfinder.h @@ -178,7 +178,7 @@ private: /** The amount remaining from srcAccount_ after the default liquidity has been removed. */ STAmount remainingAmount_; - bool convert_all_; + bool convertAll_; std::optional domain_; std::shared_ptr ledger_; diff --git a/src/xrpld/rpc/detail/RPCCall.cpp b/src/xrpld/rpc/detail/RPCCall.cpp index 24fcc652df..f405ffe4de 100644 --- a/src/xrpld/rpc/detail/RPCCall.cpp +++ b/src/xrpld/rpc/detail/RPCCall.cpp @@ -1702,19 +1702,19 @@ rpcClient( // line client works without a config file } - if (config.rpc_ip) + if (config.rpcIp) { - setup.client.ip = config.rpc_ip->address().to_string(); - setup.client.port = config.rpc_ip->port(); + setup.client.ip = config.rpcIp->address().to_string(); + setup.client.port = config.rpcIp->port(); } json::Value jvParams(json::ValueType::Array); - if (!setup.client.admin_user.empty()) - jvRequest["admin_user"] = setup.client.admin_user; + if (!setup.client.adminUser.empty()) + jvRequest["admin_user"] = setup.client.adminUser; - if (!setup.client.admin_password.empty()) - jvRequest["admin_password"] = setup.client.admin_password; + if (!setup.client.adminPassword.empty()) + jvRequest["admin_password"] = setup.client.adminPassword; if (jvRequest.isObject()) { diff --git a/src/xrpld/rpc/detail/RPCHandler.cpp b/src/xrpld/rpc/detail/RPCHandler.cpp index 6b032317d3..7e30b81fa6 100644 --- a/src/xrpld/rpc/detail/RPCHandler.cpp +++ b/src/xrpld/rpc/detail/RPCHandler.cpp @@ -135,7 +135,7 @@ fillHandler(JsonContext& context, Handler const*& result) JLOG(context.j.trace()) << "COMMAND:" << strCommand; JLOG(context.j.trace()) << "REQUEST:" << context.params; - auto handler = getHandler(context.apiVersion, context.app.config().BETA_RPC_API, strCommand); + auto handler = getHandler(context.apiVersion, context.app.config().betaRpcApi, strCommand); if (handler == nullptr) return RpcUnknownCommand; diff --git a/src/xrpld/rpc/detail/RPCSub.cpp b/src/xrpld/rpc/detail/RPCSub.cpp index 56291ab34b..92e9849939 100644 --- a/src/xrpld/rpc/detail/RPCSub.cpp +++ b/src/xrpld/rpc/detail/RPCSub.cpp @@ -39,7 +39,7 @@ public: std::string strPassword, ServiceRegistry& registry) : RPCSub(source) - , io_context_(ioContext) + , ioContext_(ioContext) , jobQueue_(jobQueue) , url_(strUrl) , username_(std::move(strUsername)) @@ -159,7 +159,7 @@ private: JLOG(j_.info()) << "RPCCall::fromNetwork: " << ip_; RPCCall::fromNetwork( - io_context_, + ioContext_, ip_, port_, username_, @@ -180,7 +180,7 @@ private: } private: - boost::asio::io_context& io_context_; + boost::asio::io_context& ioContext_; JobQueue& jobQueue_; std::string url_; diff --git a/src/xrpld/rpc/detail/Role.cpp b/src/xrpld/rpc/detail/Role.cpp index dcc6965d58..68c5fcc484 100644 --- a/src/xrpld/rpc/detail/Role.cpp +++ b/src/xrpld/rpc/detail/Role.cpp @@ -28,14 +28,14 @@ bool passwordUnrequiredOrSentCorrect(Port const& port, json::Value const& params) { XRPL_ASSERT( - !(port.admin_nets_v4.empty() && port.admin_nets_v6.empty()), + !(port.adminNetsV4.empty() && port.adminNetsV6.empty()), "xrpl::passwordUnrequiredOrSentCorrect : non-empty admin nets"); - bool const passwordRequired = (!port.admin_user.empty() || !port.admin_password.empty()); + bool const passwordRequired = (!port.adminUser.empty() || !port.adminPassword.empty()); return !passwordRequired || ((params["admin_password"].isString() && - params["admin_password"].asString() == port.admin_password) && - (params["admin_user"].isString() && params["admin_user"].asString() == port.admin_user)); + params["admin_password"].asString() == port.adminPassword) && + (params["admin_user"].isString() && params["admin_user"].asString() == port.adminUser)); } bool @@ -80,7 +80,7 @@ ipAllowed( bool isAdmin(Port const& port, json::Value const& params, beast::IP::Address const& remoteIp) { - return ipAllowed(remoteIp, port.admin_nets_v4, port.admin_nets_v6) && + return ipAllowed(remoteIp, port.adminNetsV4, port.adminNetsV6) && passwordUnrequiredOrSentCorrect(port, params); } @@ -98,7 +98,7 @@ requestRole( if (required == Role::ADMIN) return Role::FORBID; - if (ipAllowed(remoteIp.address(), port.secure_gateway_nets_v4, port.secure_gateway_nets_v6)) + if (ipAllowed(remoteIp.address(), port.secureGatewayNetsV4, port.secureGatewayNetsV6)) { if (!user.empty()) return Role::IDENTIFIED; diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index 2ed6630af2..0bdece3ec3 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -139,9 +139,9 @@ ServerHandler::ServerHandler( , jobQueue_(jobQueue) { auto const& group(cm.group("rpc")); - rpc_requests_ = group->makeCounter("requests"); - rpc_size_ = group->makeEvent("size"); - rpc_time_ = group->makeEvent("time"); + rpcRequests_ = group->makeCounter("requests"); + rpcSize_ = group->makeEvent("size"); + rpcTime_ = group->makeEvent("time"); } ServerHandler::~ServerHandler() @@ -429,7 +429,7 @@ ServerHandler::processSession( Resource::Charge loadType = Resource::kFeeReferenceRpc; try { - auto apiVersion = RPC::getAPIVersionNumber(jv, app_.config().BETA_RPC_API); + auto apiVersion = RPC::getAPIVersionNumber(jv, app_.config().betaRpcApi); if (apiVersion == RPC::kApiInvalidVersion || (!jv.isMember(jss::command) && !jv.isMember(jss::method)) || (jv.isMember(jss::command) && !jv[jss::command].isString()) || @@ -457,7 +457,7 @@ ServerHandler::processSession( auto required = RPC::roleRequired( apiVersion, - app_.config().BETA_RPC_API, + app_.config().betaRpcApi, jv.isMember(jss::command) ? jv[jss::command].asString() : jv[jss::method].asString()); auto role = requestRole( required, @@ -657,13 +657,13 @@ ServerHandler::processRequest( jsonRPC[jss::params].size() > 0 && jsonRPC[jss::params][0u].isObject()) { apiVersion = RPC::getAPIVersionNumber( - jsonRPC[jss::params][json::UInt(0)], app_.config().BETA_RPC_API); + jsonRPC[jss::params][json::UInt(0)], app_.config().betaRpcApi); } if (apiVersion == RPC::kApiVersionIfUnspecified && batch) { // for batch request, api_version may be at a different level - apiVersion = RPC::getAPIVersionNumber(jsonRPC, app_.config().BETA_RPC_API); + apiVersion = RPC::getAPIVersionNumber(jsonRPC, app_.config().betaRpcApi); } if (apiVersion == RPC::kApiInvalidVersion) @@ -686,7 +686,7 @@ ServerHandler::processRequest( if (jsonRPC.isMember(jss::method) && jsonRPC[jss::method].isString()) { required = RPC::roleRequired( - apiVersion, app_.config().BETA_RPC_API, jsonRPC[jss::method].asString()); + apiVersion, app_.config().betaRpcApi, jsonRPC[jss::method].asString()); } if (jsonRPC.isMember(jss::params) && jsonRPC[jss::params].isArray() && @@ -993,11 +993,11 @@ ServerHandler::processRequest( auto response = to_string(reply); - rpc_time_.notify( + rpcTime_.notify( std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - start)); - ++rpc_requests_; - rpc_size_.notify(beast::insight::Event::value_type{response.size()}); + ++rpcRequests_; + rpcSize_.notify(beast::insight::Event::value_type{response.size()}); response += '\n'; @@ -1060,13 +1060,13 @@ ServerHandler::Setup::makeContexts() { if (p.secure()) { - if (p.ssl_key.empty() && p.ssl_cert.empty() && p.ssl_chain.empty()) + if (p.sslKey.empty() && p.sslCert.empty() && p.sslChain.empty()) { - p.context = makeSslContext(p.ssl_ciphers); + p.context = makeSslContext(p.sslCiphers); } else { - p.context = makeSslContextAuthed(p.ssl_key, p.ssl_cert, p.ssl_chain, p.ssl_ciphers); + p.context = makeSslContextAuthed(p.sslKey, p.sslCert, p.sslChain, p.sslCiphers); } } else @@ -1106,19 +1106,19 @@ toPort(ParsedPort const& parsed, std::ostream& log) p.user = parsed.user; p.password = parsed.password; - p.admin_user = parsed.admin_user; - p.admin_password = parsed.admin_password; - p.ssl_key = parsed.ssl_key; - p.ssl_cert = parsed.ssl_cert; - p.ssl_chain = parsed.ssl_chain; - p.ssl_ciphers = parsed.ssl_ciphers; - p.pmd_options = parsed.pmd_options; - p.ws_queue_limit = parsed.ws_queue_limit; + p.adminUser = parsed.adminUser; + p.adminPassword = parsed.adminPassword; + p.sslKey = parsed.sslKey; + p.sslCert = parsed.sslCert; + p.sslChain = parsed.sslChain; + p.sslCiphers = parsed.sslCiphers; + p.pmdOptions = parsed.pmdOptions; + p.wsQueueLimit = parsed.wsQueueLimit; p.limit = parsed.limit; - p.admin_nets_v4 = parsed.admin_nets_v4; - p.admin_nets_v6 = parsed.admin_nets_v6; - p.secure_gateway_nets_v4 = parsed.secure_gateway_nets_v4; - p.secure_gateway_nets_v6 = parsed.secure_gateway_nets_v6; + p.adminNetsV4 = parsed.adminNetsV4; + p.adminNetsV6 = parsed.adminNetsV6; + p.secureGatewayNetsV4 = parsed.secureGatewayNetsV4; + p.secureGatewayNetsV6 = parsed.secureGatewayNetsV6; return p; } @@ -1221,8 +1221,8 @@ setupClient(ServerHandler::Setup& setup) setup.client.port = iter->port; setup.client.user = iter->user; setup.client.password = iter->password; - setup.client.admin_user = iter->admin_user; - setup.client.admin_password = iter->admin_password; + setup.client.adminUser = iter->adminUser; + setup.client.adminPassword = iter->adminPassword; } // Fill out the overlay portion of the Setup diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 9faa82dfe8..dd0e78c178 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -312,7 +312,7 @@ checkPayment( std::nullopt, domain, app); - if (pf.findPaths(app.config().PATH_SEARCH_OLD)) + if (pf.findPaths(app.config().pathSearchOld)) { // 4 is the maximum paths pf.computePathRanks(4); @@ -508,7 +508,7 @@ transactionPreProcessImpl( validatedLedgerAge, app.config(), app.getFeeTrack(), - getAPIVersionNumber(params, app.config().BETA_RPC_API)); + getAPIVersionNumber(params, app.config().betaRpcApi)); if (RPC::containsError(txJsonResult)) return std::move(txJsonResult); @@ -846,16 +846,16 @@ getTxFee(Application const& app, Config const& config, json::Value tx) if (tx.isMember(jss::Signers)) { if (!tx[jss::Signers].isArray()) - return config.FEES.reference_fee; + return config.fees.referenceFee; if (tx[jss::Signers].size() > STTx::kMaxMultiSigners) - return config.FEES.reference_fee; + return config.fees.referenceFee; // check multi-signed signers for (auto& signer : tx[jss::Signers]) { if (!signer.isMember(jss::Signer) || !signer[jss::Signer].isObject()) - return config.FEES.reference_fee; + return config.fees.referenceFee; if (!signer[jss::Signer].isMember(jss::SigningPubKey)) { // autofill SigningPubKey @@ -872,7 +872,7 @@ getTxFee(Application const& app, Config const& config, json::Value tx) STParsedJSONObject parsed(std::string(jss::tx_json), tx); if (!parsed.object.has_value()) { - return config.FEES.reference_fee; + return config.fees.referenceFee; } try @@ -880,13 +880,13 @@ getTxFee(Application const& app, Config const& config, json::Value tx) STTx const& stTx = STTx(std::move(parsed.object.value())); std::string reason; if (!passesLocalChecks(stTx, reason)) - return config.FEES.reference_fee; + return config.fees.referenceFee; return calculateBaseFee(*app.getOpenLedger().current(), stTx); } catch (std::exception& e) { - return config.FEES.reference_fee; + return config.fees.referenceFee; } } @@ -1305,7 +1305,7 @@ transactionSubmitMultiSigned( validatedLedgerAge, app.config(), app.getFeeTrack(), - getAPIVersionNumber(jvRequest, app.config().BETA_RPC_API)); + getAPIVersionNumber(jvRequest, app.config().betaRpcApi)); if (RPC::containsError(txJsonResult)) return std::move(txJsonResult); diff --git a/src/xrpld/rpc/detail/WSInfoSub.h b/src/xrpld/rpc/detail/WSInfoSub.h index 336f35957f..c224b93e0e 100644 --- a/src/xrpld/rpc/detail/WSInfoSub.h +++ b/src/xrpld/rpc/detail/WSInfoSub.h @@ -24,8 +24,8 @@ public: auto const& h = ws->request(); if (ipAllowed( beast::IPAddressConversion::fromAsio(ws->remoteEndpoint()).address(), - ws->port().secure_gateway_nets_v4, - ws->port().secure_gateway_nets_v6)) + ws->port().secureGatewayNetsV4, + ws->port().secureGatewayNetsV6)) { auto it = h.find("X-User"); if (it != h.end()) diff --git a/src/xrpld/rpc/handlers/orderbook/PathFind.cpp b/src/xrpld/rpc/handlers/orderbook/PathFind.cpp index 7955a93eb4..3a6b52ee98 100644 --- a/src/xrpld/rpc/handlers/orderbook/PathFind.cpp +++ b/src/xrpld/rpc/handlers/orderbook/PathFind.cpp @@ -15,7 +15,7 @@ namespace xrpl { json::Value doPathFind(RPC::JsonContext& context) { - if (context.app.config().PATH_SEARCH_MAX == 0) + if (context.app.config().pathSearchMax == 0) return rpcError(RpcNotSupported); auto lpLedger = context.ledgerMaster.getClosedLedger(); diff --git a/src/xrpld/rpc/handlers/orderbook/RipplePathFind.cpp b/src/xrpld/rpc/handlers/orderbook/RipplePathFind.cpp index 956d50add7..b7edfb6dbe 100644 --- a/src/xrpld/rpc/handlers/orderbook/RipplePathFind.cpp +++ b/src/xrpld/rpc/handlers/orderbook/RipplePathFind.cpp @@ -23,7 +23,7 @@ namespace xrpl { json::Value doRipplePathFind(RPC::JsonContext& context) { - if (context.app.config().PATH_SEARCH_MAX == 0) + if (context.app.config().pathSearchMax == 0) return rpcError(RpcNotSupported); context.loadType = Resource::kFeeHeavyBurdenRpc; diff --git a/src/xrpld/rpc/handlers/server_info/Version.h b/src/xrpld/rpc/handlers/server_info/Version.h index 64c150162e..1868df5d40 100644 --- a/src/xrpld/rpc/handlers/server_info/Version.h +++ b/src/xrpld/rpc/handlers/server_info/Version.h @@ -8,7 +8,7 @@ class VersionHandler { public: explicit VersionHandler(JsonContext& c) - : apiVersion_(c.apiVersion), betaEnabled_(c.app.config().BETA_RPC_API) + : apiVersion_(c.apiVersion), betaEnabled_(c.app.config().betaRpcApi) { } diff --git a/src/xrpld/rpc/json_body.h b/src/xrpld/rpc/json_body.h index 9f881cacbc..4520a8bf39 100644 --- a/src/xrpld/rpc/json_body.h +++ b/src/xrpld/rpc/json_body.h @@ -56,7 +56,7 @@ struct JsonBody class writer // NOLINT(readability-identifier-naming) -- Boost.Beast body concept name { - std::string body_string_; + std::string bodyString_; public: using const_buffers_type = boost::asio::const_buffer; @@ -65,7 +65,7 @@ struct JsonBody explicit writer( boost::beast::http::header const& fields, value_type const& value) - : body_string_(to_string(value)) + : bodyString_(to_string(value)) { } @@ -81,7 +81,7 @@ struct JsonBody get(boost::beast::error_code& ec) { ec.assign(0, ec.category()); - return {{const_buffers_type{body_string_.data(), body_string_.size()}, false}}; + return {{const_buffers_type{bodyString_.data(), bodyString_.size()}, false}}; } }; }; From 28cc20c81679fd8bb9aa3ac85e450d1daf387816 Mon Sep 17 00:00:00 2001 From: Shawn Xie <35279399+shawnxie999@users.noreply.github.com> Date: Thu, 21 May 2026 02:19:04 -0400 Subject: [PATCH 07/87] fix: Fix wrong hybrid offer orderbook placement and update `LedgerStateFix` to amend `ExchangeRate` meta (#7087) Co-authored-by: Peter Chen --- .../xrpl/protocol/detail/transactions.macro | 1 + .../transactions/LedgerStateFix.h | 37 +++ .../xrpl/tx/invariants/DirectoryInvariant.h | 27 ++ include/xrpl/tx/invariants/InvariantCheck.h | 2 + include/xrpl/tx/transactors/dex/OfferCreate.h | 1 + .../tx/transactors/system/LedgerStateFix.h | 1 + .../tx/invariants/DirectoryInvariant.cpp | 96 +++++++ .../tx/transactors/dex/OfferCreate.cpp | 13 +- .../tx/transactors/system/LedgerStateFix.cpp | 86 +++++- src/test/app/FixNFTokenPageLinks_test.cpp | 7 + src/test/app/Invariants_test.cpp | 103 +++++++ src/test/app/PermissionedDEX_test.cpp | 262 ++++++++++++++++++ src/test/jtx/impl/ledgerStateFixes.cpp | 13 + src/test/jtx/ledgerStateFix.h | 4 + .../transactions/LedgerStateFixTests.cpp | 21 ++ 15 files changed, 669 insertions(+), 5 deletions(-) create mode 100644 include/xrpl/tx/invariants/DirectoryInvariant.h create mode 100644 src/libxrpl/tx/invariants/DirectoryInvariant.cpp diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 9bac9ef7db..450e2558cc 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -688,6 +688,7 @@ TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, ({ {sfLedgerFixType, SoeRequired}, {sfOwner, SoeOptional}, + {sfBookDirectory, SoeOptional}, })) /** This transaction type creates a MPTokensIssuance instance */ diff --git a/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h index 3c58815a02..52723ad5eb 100644 --- a/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h +++ b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h @@ -83,6 +83,32 @@ public: { return this->tx_->isFieldPresent(sfOwner); } + + /** + * @brief Get sfBookDirectory (SoeOptional) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getBookDirectory() const + { + if (hasBookDirectory()) + { + return this->tx_->at(sfBookDirectory); + } + return std::nullopt; + } + + /** + * @brief Check if sfBookDirectory is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasBookDirectory() const + { + return this->tx_->isFieldPresent(sfBookDirectory); + } }; /** @@ -149,6 +175,17 @@ public: return *this; } + /** + * @brief Set sfBookDirectory (SoeOptional) + * @return Reference to this builder for method chaining. + */ + LedgerStateFixBuilder& + setBookDirectory(std::decay_t const& value) + { + object_[sfBookDirectory] = value; + return *this; + } + /** * @brief Build and return the LedgerStateFix wrapper. * @param publicKey The public key for signing. diff --git a/include/xrpl/tx/invariants/DirectoryInvariant.h b/include/xrpl/tx/invariants/DirectoryInvariant.h new file mode 100644 index 0000000000..96643ea465 --- /dev/null +++ b/include/xrpl/tx/invariants/DirectoryInvariant.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +class ValidBookDirectory +{ + bool badBookDirectory_ = false; + hash_set rootIndexes_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); +}; + +} // namespace xrpl diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index bc9608fd56..a58ac0df50 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -393,6 +394,7 @@ using InvariantChecks = std::tuple< ValidMPTIssuance, ValidPermissionedDomain, ValidPermissionedDEX, + ValidBookDirectory, ValidAMM, NoModifiedUnmodifiableFields, ValidPseudoAccounts, diff --git a/include/xrpl/tx/transactors/dex/OfferCreate.h b/include/xrpl/tx/transactors/dex/OfferCreate.h index d1ac8b5626..efae5312e2 100644 --- a/include/xrpl/tx/transactors/dex/OfferCreate.h +++ b/include/xrpl/tx/transactors/dex/OfferCreate.h @@ -85,6 +85,7 @@ private: Keylet const& offerIndex, STAmount const& saTakerPays, STAmount const& saTakerGets, + std::uint64_t openRate, std::function)> const& setDir); }; diff --git a/include/xrpl/tx/transactors/system/LedgerStateFix.h b/include/xrpl/tx/transactors/system/LedgerStateFix.h index 8d704467eb..6fbae6fc6a 100644 --- a/include/xrpl/tx/transactors/system/LedgerStateFix.h +++ b/include/xrpl/tx/transactors/system/LedgerStateFix.h @@ -9,6 +9,7 @@ class LedgerStateFix : public Transactor public: enum class FixType : std::uint16_t { NfTokenPageLink = 1, + BookExchangeRate = 2, }; static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal; diff --git a/src/libxrpl/tx/invariants/DirectoryInvariant.cpp b/src/libxrpl/tx/invariants/DirectoryInvariant.cpp new file mode 100644 index 0000000000..2a4fac07d0 --- /dev/null +++ b/src/libxrpl/tx/invariants/DirectoryInvariant.cpp @@ -0,0 +1,96 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +namespace { + +[[nodiscard]] bool +isRootBookDirectory(SLE const& dir) +{ + // Child page keys do not encode book quality. + return dir.isFieldPresent(sfExchangeRate) || dir.isFieldPresent(sfTakerPaysCurrency) || + dir.isFieldPresent(sfTakerPaysIssuer) || dir.isFieldPresent(sfTakerPaysMPT) || + dir.isFieldPresent(sfTakerGetsCurrency) || dir.isFieldPresent(sfTakerGetsIssuer) || + dir.isFieldPresent(sfTakerGetsMPT) || dir.isFieldPresent(sfDomainID); +} + +[[nodiscard]] bool +badExchangeRate(SLE const& dir) +{ + return isRootBookDirectory(dir) && + (!dir.isFieldPresent(sfExchangeRate) || + dir.getFieldU64(sfExchangeRate) != getQuality(dir.key())); +} + +} // namespace + +void +ValidBookDirectory::visitEntry( + bool, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + // New root directories must have matching exchange-rate metadata. New + // child directories must point to an existing root. + + // Only validate newly-created directories; LedgerStateFix handles legacy + // bad exchange-rate metadata. + if (badBookDirectory_ || before || !after || after->getType() != ltDIR_NODE) + return; + + auto const rootIndex = after->getFieldH256(sfRootIndex); + if (after->key() == rootIndex && !badBookDirectory_) + { + badBookDirectory_ = badBookDirectory_ || badExchangeRate(*after); + return; + } + + rootIndexes_.insert(rootIndex); +} + +bool +ValidBookDirectory::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + if (!view.rules().enabled(fixCleanup3_2_0)) + return true; + + if (badBookDirectory_) + { + JLOG(j.fatal()) << "Invariant failed: book directory exchange rate " + "does not match directory quality"; + return false; + } + + for (auto const& rootIndex : rootIndexes_) + { + auto const root = view.read(Keylet(ltDIR_NODE, rootIndex)); + if (!root) + { + JLOG(j.fatal()) << "Invariant failed: book directory root missing"; + return false; + } + } + + return true; +} + +} // namespace xrpl diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index 69c101644c..ed9eb1255d 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -547,6 +547,7 @@ OfferCreate::applyHybrid( Keylet const& offerKey, STAmount const& saTakerPays, STAmount const& saTakerGets, + std::uint64_t openRate, std::function)> const& setDir) { if (!sleOffer->isFieldPresent(sfDomainID)) @@ -558,7 +559,7 @@ OfferCreate::applyHybrid( // if offer is hybrid, need to also place into open offer dir Book const book{saTakerPays.asset(), saTakerGets.asset(), std::nullopt}; - auto dir = keylet::quality(keylet::kBook(book), getRate(saTakerGets, saTakerPays)); + auto dir = keylet::quality(keylet::kBook(book), openRate); bool const bookExists = sb.exists(dir); auto const bookNode = sb.dirAppend(dir, offerKey, [&](SLE::ref sle) { @@ -924,8 +925,16 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) // if it's a hybrid offer, set hybrid flag, and create an open dir if (bHybrid) { + // Pre-fixCleanup3_2_0: the open-book directory quality was computed + // from post-crossing amounts, which may differ from the original rate + // due to rounding in rate preservation. Post-fixCleanup3_2_0: use the + // original placement rate so the open-book directory quality matches + // the domain-book directory. + auto const openRate = ctx_.view().rules().enabled(fixCleanup3_2_0) + ? uRate + : getRate(saTakerGets, saTakerPays); auto const res = - applyHybrid(sb, sleOffer, offerIndex, saTakerPays, saTakerGets, setBookDir); + applyHybrid(sb, sleOffer, offerIndex, saTakerPays, saTakerGets, openRate, setBookDir); if (!isTesSuccess(res)) return {res, true}; // LCOV_EXCL_LINE } diff --git a/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp index 2f30311e6f..222e5dce7a 100644 --- a/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp +++ b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -12,24 +14,72 @@ #include #include +#include +#include #include +#include namespace xrpl { +namespace { + +using FixType = LedgerStateFix::FixType; + +std::array, 2> const kLedgerFixFields = {{ + {FixType::NfTokenPageLink, &sfOwner}, + {FixType::BookExchangeRate, &sfBookDirectory}, +}}; + +[[nodiscard]] SField const* +fixField(FixType const fixType) +{ + auto const iter = std::ranges::find_if( + kLedgerFixFields, [fixType](auto const& entry) { return entry.first == fixType; }); + + if (iter == kLedgerFixFields.end()) + return nullptr; // LCOV_EXCL_LINE + + return iter->second; +} + +[[nodiscard]] bool +hasUnexpectedFixField(STTx const& tx, SField const& expected) +{ + return std::ranges::any_of(kLedgerFixFields, [&tx, &expected](auto const& entry) { + auto const field = entry.second; + return field != &expected && tx.isFieldPresent(*field); + }); +} + +} // namespace + NotTEC LedgerStateFix::preflight(PreflightContext const& ctx) { - switch (static_cast(ctx.tx[sfLedgerFixType])) + auto const fixType = static_cast(ctx.tx[sfLedgerFixType]); + + switch (fixType) { case FixType::NfTokenPageLink: - if (!ctx.tx.isFieldPresent(sfOwner)) - return temINVALID; + break; + + case FixType::BookExchangeRate: + if (!ctx.rules.enabled(fixCleanup3_2_0)) + return temDISABLED; break; default: return tefINVALID_LEDGER_FIX_TYPE; } + auto const expectedField = fixField(fixType); + if (expectedField == nullptr) + return tefINVALID_LEDGER_FIX_TYPE; // LCOV_EXCL_LINE + + // Each fix type allows exactly one fix-specific field. + if (!ctx.tx.isFieldPresent(*expectedField) || hasUnexpectedFixField(ctx.tx, *expectedField)) + return temINVALID; + return tesSUCCESS; } @@ -53,6 +103,24 @@ LedgerStateFix::preclaim(PreclaimContext const& ctx) return tesSUCCESS; } + if (static_cast(ctx.tx[sfLedgerFixType]) == FixType::BookExchangeRate) + { + auto const dirKey = ctx.tx.getFieldH256(sfBookDirectory); + auto const sle = ctx.view.read(Keylet(ltDIR_NODE, dirKey)); + if (!sle) + return tecOBJECT_NOT_FOUND; + + // Must be the first page of a book directory (has sfExchangeRate). + if (!sle->isFieldPresent(sfExchangeRate)) + return tecNO_PERMISSION; + + // ExchangeRate is already correct, nothing to fix. + if (getQuality(sle->key()) == sle->getFieldU64(sfExchangeRate)) + return tecNO_PERMISSION; + + return tesSUCCESS; + } + // preflight is supposed to verify that only valid FixTypes get to preclaim. return tecINTERNAL; // LCOV_EXCL_LINE } @@ -68,6 +136,18 @@ LedgerStateFix::doApply() return tesSUCCESS; } + if (static_cast(ctx_.tx[sfLedgerFixType]) == FixType::BookExchangeRate) + { + auto const dirKey = ctx_.tx.getFieldH256(sfBookDirectory); + auto sle = view().peek(Keylet(ltDIR_NODE, dirKey)); + if (!sle) + return tecINTERNAL; // LCOV_EXCL_LINE + + sle->setFieldU64(sfExchangeRate, getQuality(sle->key())); + view().update(sle); + return tesSUCCESS; + } + // preflight is supposed to verify that only valid FixTypes get to doApply. return tecINTERNAL; // LCOV_EXCL_LINE } diff --git a/src/test/app/FixNFTokenPageLinks_test.cpp b/src/test/app/FixNFTokenPageLinks_test.cpp index f77d924c94..cfe2fd1808 100644 --- a/src/test/app/FixNFTokenPageLinks_test.cpp +++ b/src/test/app/FixNFTokenPageLinks_test.cpp @@ -173,6 +173,13 @@ class FixNFTokenPageLinks_test : public beast::unit_test::Suite tx.removeMember(sfOwner.jsonName); env(tx, Fee(linkFixFee), Ter(temINVALID)); } + { + // NFTokenPageLink fixes require sfOwner and reject fields that + // belong to other LedgerStateFix types. + json::Value tx = ledgerStateFix::nftPageLinks(alice, alice); + tx[sfBookDirectory.jsonName] = to_string(uint256{1}); + env(tx, Fee(linkFixFee), Ter(temINVALID)); + } { // Invalid LedgerFixType codes. json::Value tx = ledgerStateFix::nftPageLinks(alice, alice); diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 21094e0142..5cb872f291 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ #include #include #include +#include #include #include @@ -2037,6 +2039,106 @@ class Invariants_test : public beast::unit_test::Suite } } + void + testBookDirectoryExchangeRate() + { + using namespace test::jtx; + testcase << "book directory exchange rate"; + + auto const getBookRootKey = [](Account const& account, std::uint64_t quality) { + Book const book{xrpIssue(), account["USD"], std::nullopt}; + return keylet::quality(keylet::kBook(book), quality); + }; + + // Root book-directory pages carry exchange-rate metadata that must + // match the quality encoded in the directory key. + auto const makeRootPage = [](Keylet const& dir, std::uint64_t exchangeRate) { + auto sleDir = std::make_shared(dir); + sleDir->setFieldH256(sfRootIndex, dir.key); + STVector256 indexes; + indexes.pushBack(uint256{1}); + sleDir->setFieldV256(sfIndexes, indexes); + sleDir->setFieldU64(sfExchangeRate, exchangeRate); + return sleDir; + }; + + // Child pages do not carry quality metadata; they only point back to + // the root directory. + auto const makeChildPage = [](Keylet const& rootDir) { + auto sleDir = std::make_shared(keylet::page(rootDir, 1)); + sleDir->setFieldH256(sfRootIndex, rootDir.key); + STVector256 indexes; + indexes.pushBack(uint256{2}); + sleDir->setFieldV256(sfIndexes, indexes); + return sleDir; + }; + + auto const makeOfferCreateTx = [] { + return STTx{ttOFFER_CREATE, [](STObject& tx) { + Account const account{"A1"}; + tx.setFieldAmount(sfTakerPays, XRP(1)); + tx.setFieldAmount(sfTakerGets, account["USD"](1)); + }}; + }; + std::initializer_list const failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}; + + // Creating a root book directory with mismatched exchange-rate + // metadata violates the invariant. + doInvariantCheck( + {{"book directory exchange rate does not match directory quality"}}, + [&](Account const& a1, Account const&, ApplyContext& ac) { + auto const directoryQuality = STAmount::kURateOne; + auto const dir = getBookRootKey(a1, directoryQuality); + ac.view().insert(makeRootPage(dir, directoryQuality + 1)); + return true; + }, + XRPAmount{}, + makeOfferCreateTx(), + failTers); + + // A new child page must point to an existing root page. + doInvariantCheck( + {{"book directory root missing"}}, + [&](Account const& a1, Account const&, ApplyContext& ac) { + auto const directoryQuality = STAmount::kURateOne; + auto const rootDir = getBookRootKey(a1, directoryQuality); + // Insert only the child page. It points at rootDir, but the + // corresponding root page is intentionally missing. + ac.view().insert(makeChildPage(rootDir)); + return true; + }, + XRPAmount{}, + makeOfferCreateTx(), + failTers); + + // Legacy bad-root tolerance: + // - The view contains a pre-existing root page with bad sfExchangeRate + // metadata. + // - The simulated transaction only creates a child page pointing to + // that root. + // - The invariant must pass because this transaction did not create + // the bad root, only adding a child page. + { + Env env{*this, defaultAmendments()}; + Account const a1{"A1"}; + env.fund(XRP(1000), a1); + env.close(); + + OpenView view{*env.current()}; + auto const directoryQuality = STAmount::kURateOne; + auto const rootDir = getBookRootKey(a1, directoryQuality); + view.rawInsert(makeRootPage(rootDir, directoryQuality + 1)); + + ValidBookDirectory invariant; + invariant.visitEntry(false, nullptr, makeChildPage(rootDir)); + + test::StreamSink sink{beast::Severity::Warning}; + beast::Journal const jlog{sink}; + BEAST_EXPECT( + invariant.finalize(makeOfferCreateTx(), tesSUCCESS, XRPAmount{}, view, jlog)); + } + } + Keylet createLoanBroker(jtx::Account const& a, jtx::Env& env, jtx::PrettyAsset const& asset) { @@ -4489,6 +4591,7 @@ public: testPermissionedDomainInvariants(defaultAmendments() - fixCleanup3_1_3); testPermissionedDEX(defaultAmendments() | fixCleanup3_1_3); testPermissionedDEX(defaultAmendments() - fixCleanup3_1_3); + testBookDirectoryExchangeRate(); testNoModifiedUnmodifiableFields(); testValidPseudoAccounts(); testValidLoanBroker(); diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index 3a95fb2f92..c6e94d7994 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include // IWYU pragma: keep #include @@ -1457,6 +1459,263 @@ class PermissionedDEX_test : public beast::unit_test::Suite } } + void + testHybridOfferCrossingQuality(FeatureBitset features) + { + bool const fixEnabled = features[fixCleanup3_2_0]; + testcase << "Hybrid offer crossing quality" + << (fixEnabled ? " (fixCleanup3_2_0)" : " (pre-fix)"); + + // Partially-crossed hybrid offer should have consistent quality + // across both book directories. + // + // Steps: + // - Bob places a hybrid offer. + // - Alice places an opposing hybrid offer that partially crosses. + // + // Verify: + // - Domain-book key quality == its sfExchangeRate. + // - Post-fix: open-book key quality == domain-book key quality. + // - Pre-fix: open-book key quality != domain-book key quality + // (key used post-crossing rate, sfExchangeRate used pre-crossing). + + Env env(*this, features); + auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + PermissionedDEX(env); + + // Bob places a hybrid offer: TakerPays = XRP(100), TakerGets = USD(40) + auto const bobOfferSeq{env.seq(bob_)}; + env(offer(bob_, XRP(100), USD(40)), Txflags(tfHybrid), Domain(domainID)); + env.close(); + BEAST_EXPECT(offerExists(env, bob_, bobOfferSeq)); + + // Alice places a hybrid offer in the opposite direction that + // partially crosses Bob's offer. + // Alice: TakerPays = USD(100), TakerGets = XRP(300) (rate = 3 XRP/USD) + // Bob's offer is at a better rate (2.5 XRP/USD) so crossing occurs. + auto const aliceOfferSeq{env.seq(alice_)}; + env(offer(alice_, USD(100), XRP(300)), Txflags(tfHybrid), Domain(domainID)); + env.close(); + + // After crossing, Alice's remaining offer should be placed. + auto const sle = env.le(keylet::offer(alice_.id(), aliceOfferSeq)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->isFieldPresent(sfAdditionalBooks)); + BEAST_EXPECT(sle->getFieldArray(sfAdditionalBooks).size() == 1); + + auto const domainDirKey = sle->getFieldH256(sfBookDirectory); + auto const openDirKey = + sle->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory); + + auto const domainQuality = getQuality(domainDirKey); + auto const openQuality = getQuality(openDirKey); + + // Read the directory SLEs and check sfExchangeRate vs key quality. + auto const domainDirSle = env.le(Keylet(ltDIR_NODE, domainDirKey)); + auto const openDirSle = env.le(Keylet(ltDIR_NODE, openDirKey)); + BEAST_EXPECT(domainDirSle); + BEAST_EXPECT(openDirSle); + + auto const domainExRate = domainDirSle->getFieldU64(sfExchangeRate); + auto const openExRate = openDirSle->getFieldU64(sfExchangeRate); + auto const preCrossingQuality = std::uint64_t{5623825668291712342ULL}; + auto const postCrossingQuality = std::uint64_t{5623825668291712341ULL}; + + // Domain directory: sfExchangeRate should always match key quality + // (both use the pre-crossing rate). Correct behavior. + BEAST_EXPECT(domainQuality == preCrossingQuality); + BEAST_EXPECT(domainExRate == preCrossingQuality); + BEAST_EXPECT(domainExRate == domainQuality); + + if (fixEnabled) + { + // Correct behavior: both directory keys use the pre-crossing rate. + BEAST_EXPECT(openQuality == preCrossingQuality); + BEAST_EXPECT(domainQuality == openQuality); + + // sfExchangeRate matches key quality on both directories. + BEAST_EXPECT(openExRate == preCrossingQuality); + BEAST_EXPECT(openExRate == openQuality); + } + else + { + // Wrong legacy behavior: the open-book directory key uses the + // post-crossing rate instead of the domain-book rate. + BEAST_EXPECT(openQuality == postCrossingQuality); + BEAST_EXPECT(domainQuality != openQuality); + + // The open-book sfExchangeRate still uses the pre-crossing rate, + // so it no longer matches the actual quality encoded in the + // open-book directory key. + BEAST_EXPECT(openExRate == preCrossingQuality); + BEAST_EXPECT(openExRate != openQuality); + BEAST_EXPECT(openExRate == domainQuality); + } + } + + void + testBookExchangeRateFix(FeatureBitset features) + { + testcase("LedgerStateFix BookExchangeRate"); + + // Use the pre-fix path to create a hybrid offer with a mismatched + // sfExchangeRate, then apply LedgerStateFix to correct it. + // + // Steps: + // - Create a partially-crossed hybrid offer (pre-fixCleanup3_2_0) + // so the open-book directory has wrong sfExchangeRate. + // - Re-enable fixCleanup3_2_0 and submit a LedgerStateFix to + // repair the open-book directory's sfExchangeRate. + // + // Verify: + // - Before fix: sfExchangeRate != getQuality(key). + // - After fix: sfExchangeRate == getQuality(key). + + { + // Amendment gate: BookExchangeRate fixes require fixCleanup3_2_0. + Env env(*this, features - fixCleanup3_2_0); + Account const carol{"carol"}; + + env.fund(XRP(1000), carol); + env.close(); + + env(ledgerStateFix::bookExchangeRate(carol, uint256{1}), Ter(temDISABLED)); + } + + { + // Preflight check: BookExchangeRate fixes only accept their + // required fix-specific field. + Env env(*this, features); + Account const carol{"carol"}; + + env.fund(XRP(1000), carol); + env.close(); + + // BookExchangeRate fixes require sfBookDirectory. + auto missingBookDirectory = ledgerStateFix::bookExchangeRate(carol, uint256{1}); + missingBookDirectory.removeMember(sfBookDirectory.jsonName); + env(missingBookDirectory, Ter(temINVALID)); + + // BookExchangeRate fixes reject fields that belong to other + // LedgerStateFix types. + auto extraOwner = ledgerStateFix::bookExchangeRate(carol, uint256{1}); + extraOwner[sfOwner.jsonName] = carol.human(); + env(extraOwner, Ter(temINVALID)); + } + + { + Env env(*this, features); + auto const setup = PermissionedDEX(env); + auto const fixFee = drops(env.current()->fees().increment); + + { + // Preclaim check: the target directory must exist. + env(ledgerStateFix::bookExchangeRate(setup.carol, uint256{1}), + Fee(fixFee), + Ter(tecOBJECT_NOT_FOUND)); + } + + { + // Preclaim check: the target directory must be a book root + // page. Owner directories are ltDIR_NODE entries, but they do + // not carry sfExchangeRate. + auto const ownerDir = keylet::ownerDir(setup.bob.id()); + auto const ownerDirSle = env.le(ownerDir); + BEAST_EXPECT(ownerDirSle); + BEAST_EXPECT(!ownerDirSle->isFieldPresent(sfExchangeRate)); + + env(ledgerStateFix::bookExchangeRate(setup.carol, ownerDir.key), + Fee(fixFee), + Ter(tecNO_PERMISSION)); + } + + { + // Preclaim check: a correct sfExchangeRate leaves nothing to + // repair. + auto const bobOfferSeq{env.seq(setup.bob)}; + env(offer(setup.bob, XRP(100), setup.usd(40))); + env.close(); + + auto const sle = env.le(keylet::offer(setup.bob.id(), bobOfferSeq)); + BEAST_EXPECT(sle); + + auto const dirKey = sle->getFieldH256(sfBookDirectory); + { + auto const dirSle = env.le(Keylet(ltDIR_NODE, dirKey)); + BEAST_EXPECT(dirSle); + auto const exchangeRate = dirSle->getFieldU64(sfExchangeRate); + auto const quality = getQuality(dirKey); + BEAST_EXPECT(exchangeRate == quality); + } + + env(ledgerStateFix::bookExchangeRate(setup.carol, dirKey), + Fee(fixFee), + Ter(tecNO_PERMISSION)); + } + } + + { + // Repair path: start without fixCleanup3_2_0 to produce the + // mismatch, then enable the amendment and fix it. + Env env(*this, features - fixCleanup3_2_0); + auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + PermissionedDEX(env); + + // Bob places a hybrid offer. + env(offer(bob_, XRP(100), USD(40)), Txflags(tfHybrid), Domain(domainID)); + env.close(); + + // Alice partially crosses Bob. + auto const aliceOfferSeq{env.seq(alice_)}; + env(offer(alice_, USD(100), XRP(300)), Txflags(tfHybrid), Domain(domainID)); + env.close(); + + auto const sle = env.le(keylet::offer(alice_.id(), aliceOfferSeq)); + BEAST_EXPECT(sle); + + auto const openDirKey = + sle->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory); + + auto const preCrossingQuality = std::uint64_t{5623825668291712342ULL}; + auto const postCrossingQuality = std::uint64_t{5623825668291712341ULL}; + + // Confirm mismatch exists. + { + auto const dirSle = env.le(Keylet(ltDIR_NODE, openDirKey)); + BEAST_EXPECT(dirSle); + auto const exchangeRate = dirSle->getFieldU64(sfExchangeRate); + auto const quality = getQuality(openDirKey); + BEAST_EXPECT(exchangeRate == preCrossingQuality); + BEAST_EXPECT(quality == postCrossingQuality); + BEAST_EXPECT(exchangeRate != quality); + } + + // Enable fixCleanup3_2_0 and apply the LedgerStateFix. + env.enableFeature(fixCleanup3_2_0); + env.close(); + + auto const fixFee = drops(env.current()->fees().increment); + env(ledgerStateFix::bookExchangeRate(carol_, openDirKey), Fee(fixFee)); + env.close(); + + // Confirm sfExchangeRate now matches the key quality. + { + auto const dirSle = env.le(Keylet(ltDIR_NODE, openDirKey)); + BEAST_EXPECT(dirSle); + auto const exchangeRate = dirSle->getFieldU64(sfExchangeRate); + auto const quality = getQuality(openDirKey); + BEAST_EXPECT(exchangeRate == postCrossingQuality); + BEAST_EXPECT(quality == postCrossingQuality); + BEAST_EXPECT(exchangeRate == quality); + } + + // Submitting again should fail — nothing to fix. + env(ledgerStateFix::bookExchangeRate(carol_, openDirKey), + Fee(fixFee), + Ter(tecNO_PERMISSION)); + } + } + void testCancelRegularOfferWithDomainCreate(FeatureBitset features) { @@ -1528,6 +1787,9 @@ public: testHybridOfferDirectories(all); testHybridMalformedOffer(all); testHybridMalformedOffer(all - fixCleanup3_1_3); + testHybridOfferCrossingQuality(all); + testHybridOfferCrossingQuality(all - fixCleanup3_2_0); + testBookExchangeRateFix(all); // Cancelling a regular offer in a domain OfferCreate is allowed // only after fixCleanup3_2_0. diff --git a/src/test/jtx/impl/ledgerStateFixes.cpp b/src/test/jtx/impl/ledgerStateFixes.cpp index a4b6d9b986..30c6659124 100644 --- a/src/test/jtx/impl/ledgerStateFixes.cpp +++ b/src/test/jtx/impl/ledgerStateFixes.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -22,4 +23,16 @@ nftPageLinks(jtx::Account const& acct, jtx::Account const& owner) return jv; } +// Fix sfExchangeRate on a book directory. acct pays fee. +json::Value +bookExchangeRate(jtx::Account const& acct, uint256 const& bookDir) +{ + json::Value jv; + jv[sfAccount.jsonName] = acct.human(); + jv[sfLedgerFixType.jsonName] = static_cast(LedgerStateFix::FixType::BookExchangeRate); + jv[sfBookDirectory.jsonName] = to_string(bookDir); + jv[sfTransactionType.jsonName] = jss::LedgerStateFix; + return jv; +} + } // namespace xrpl::test::jtx::ledgerStateFix diff --git a/src/test/jtx/ledgerStateFix.h b/src/test/jtx/ledgerStateFix.h index 769a28fb08..71f3f76101 100644 --- a/src/test/jtx/ledgerStateFix.h +++ b/src/test/jtx/ledgerStateFix.h @@ -10,4 +10,8 @@ namespace xrpl::test::jtx::ledgerStateFix { json::Value nftPageLinks(jtx::Account const& acct, jtx::Account const& owner); +/** Repair sfExchangeRate on a book directory's first page. */ +json::Value +bookExchangeRate(jtx::Account const& acct, uint256 const& bookDir); + } // namespace xrpl::test::jtx::ledgerStateFix diff --git a/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp index 60034255c7..5b78f0cfdd 100644 --- a/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/transactions/LedgerStateFixTests.cpp @@ -31,6 +31,7 @@ TEST(TransactionsLedgerStateFixTests, BuilderSettersRoundTrip) // Transaction-specific field values auto const ledgerFixTypeValue = canonical_UINT16(); auto const ownerValue = canonical_ACCOUNT(); + auto const bookDirectoryValue = canonical_UINT256(); LedgerStateFixBuilder builder{ accountValue, @@ -41,6 +42,7 @@ TEST(TransactionsLedgerStateFixTests, BuilderSettersRoundTrip) // Set optional fields builder.setOwner(ownerValue); + builder.setBookDirectory(bookDirectoryValue); auto tx = builder.build(publicKey, secretKey); @@ -72,6 +74,14 @@ TEST(TransactionsLedgerStateFixTests, BuilderSettersRoundTrip) EXPECT_TRUE(tx.hasOwner()); } + { + auto const& expected = bookDirectoryValue; + auto const actualOpt = tx.getBookDirectory(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBookDirectory should be present"; + expectEqualField(expected, *actualOpt, "sfBookDirectory"); + EXPECT_TRUE(tx.hasBookDirectory()); + } + } // 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, @@ -90,6 +100,7 @@ TEST(TransactionsLedgerStateFixTests, BuilderFromStTxRoundTrip) // Transaction-specific field values auto const ledgerFixTypeValue = canonical_UINT16(); auto const ownerValue = canonical_ACCOUNT(); + auto const bookDirectoryValue = canonical_UINT256(); // Build an initial transaction LedgerStateFixBuilder initialBuilder{ @@ -100,6 +111,7 @@ TEST(TransactionsLedgerStateFixTests, BuilderFromStTxRoundTrip) }; initialBuilder.setOwner(ownerValue); + initialBuilder.setBookDirectory(bookDirectoryValue); auto initialTx = initialBuilder.build(publicKey, secretKey); @@ -131,6 +143,13 @@ TEST(TransactionsLedgerStateFixTests, BuilderFromStTxRoundTrip) expectEqualField(expected, *actualOpt, "sfOwner"); } + { + auto const& expected = bookDirectoryValue; + auto const actualOpt = rebuiltTx.getBookDirectory(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfBookDirectory should be present"; + expectEqualField(expected, *actualOpt, "sfBookDirectory"); + } + } // 3) Verify wrapper throws when constructed from wrong transaction type. @@ -190,6 +209,8 @@ TEST(TransactionsLedgerStateFixTests, OptionalFieldsReturnNullopt) // Verify optional fields are not present EXPECT_FALSE(tx.hasOwner()); EXPECT_FALSE(tx.getOwner().has_value()); + EXPECT_FALSE(tx.hasBookDirectory()); + EXPECT_FALSE(tx.getBookDirectory().has_value()); } } From afcf6fbcdcdaaccb3dfa66b7c0a0c3d570bed24a Mon Sep 17 00:00:00 2001 From: Rithvik Reddygari <67663680+ricky122-5@users.noreply.github.com> Date: Thu, 21 May 2026 02:33:19 -0400 Subject: [PATCH 08/87] docs: Add --parallel flag to cmake build commands in BUILD.md (#7302) --- BUILD.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/BUILD.md b/BUILD.md index 632832c002..a1163dbfcc 100644 --- a/BUILD.md +++ b/BUILD.md @@ -427,16 +427,19 @@ install ccache --version 4.11.3 --allow-downgrade`. Single-config generators: ``` - cmake --build . + cmake --build . --parallel N ``` Multi-config generators: ``` - cmake --build . --config Release - cmake --build . --config Debug + cmake --build . --config Release --parallel N + cmake --build . --config Debug --parallel N ``` + Replace the `--parallel` parameter N with the desired number of parallel jobs. A common starting point is half of the number of available CPU + cores. + 5. Test xrpld. Single-config generators: From f6fd5ddb0a08120f7d56c4e56ac603111ee85ba9 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Thu, 21 May 2026 14:24:04 +0100 Subject: [PATCH 09/87] fix: Add null check (#7305) Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 --- src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp index e6c7a160ad..ae551de1ab 100644 --- a/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp +++ b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp @@ -51,7 +51,6 @@ iteratePriceData( std::shared_ptr const& sle, std::function const& f) { - using Meta = std::shared_ptr; static constexpr std::uint8_t kMaxHistory = 3; bool isNew = false; std::uint8_t history = 0; @@ -73,7 +72,7 @@ iteratePriceData( // for the Oracle is not found in the inner loop STObject const* prevChain = nullptr; - Meta meta = nullptr; + std::shared_ptr meta = nullptr; while (true) { if (prevChain == chain) @@ -93,6 +92,8 @@ iteratePriceData( return; // LCOV_EXCL_LINE meta = ledger->txRead(prevTx).second; + if (!meta) + return; prevChain = chain; for (STObject const& node : meta->getFieldArray(sfAffectedNodes)) From 795dc5e3643b4d66314f1240dc6728cfa71f11c3 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Thu, 21 May 2026 16:46:26 +0200 Subject: [PATCH 10/87] fix: Avoid principal-zeroing in non-final loan payments at coarse scale (#7050) Co-authored-by: Ed Hennis --- src/libxrpl/ledger/helpers/LendingHelpers.cpp | 17 +- src/test/app/Loan_test.cpp | 388 ++++++++++++------ 2 files changed, 278 insertions(+), 127 deletions(-) diff --git a/src/libxrpl/ledger/helpers/LendingHelpers.cpp b/src/libxrpl/ledger/helpers/LendingHelpers.cpp index 26e320c15d..2c756c2877 100644 --- a/src/libxrpl/ledger/helpers/LendingHelpers.cpp +++ b/src/libxrpl/ledger/helpers/LendingHelpers.cpp @@ -1058,11 +1058,22 @@ computePaymentComponents( rules, periodicPayment, periodicRate, paymentRemaining - 1, managementFeeRate); // Round the target to the loan's scale to match how actual loan values - // are stored. + // are stored. With fixCleanup3_2_0 enabled, principal is rounded upward + // and interest downward so that at coarse scale principal sticks at the + // floor (until the final payment clears it) while interest absorbs each + // periodic payment. Without the amendment the pre-existing round-to- + // nearest behavior is preserved (which can hit the "Partial principal + // payment" assertion on degenerate integer-scale loans). + bool const fixCleanup320Enabled = rules.enabled(fixCleanup3_2_0); + Number::RoundingMode const principalRounding = + fixCleanup320Enabled ? Number::RoundingMode::Upward : Number::getround(); + Number::RoundingMode const interestRounding = + fixCleanup320Enabled ? Number::RoundingMode::Downward : Number::getround(); LoanState const roundedTarget = LoanState{ .valueOutstanding = roundToAsset(asset, trueTarget.valueOutstanding, scale), - .principalOutstanding = roundToAsset(asset, trueTarget.principalOutstanding, scale), - .interestDue = roundToAsset(asset, trueTarget.interestDue, scale), + .principalOutstanding = + roundToAsset(asset, trueTarget.principalOutstanding, scale, principalRounding), + .interestDue = roundToAsset(asset, trueTarget.interestDue, scale, interestRounding), .managementFeeDue = roundToAsset(asset, trueTarget.managementFeeDue, scale)}; // Get the current actual loan state from the ledger values diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 201bb6942a..b5687265ab 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -90,8 +91,25 @@ class Loan_test : public beast::unit_test::Suite protected: // Ensure that all the features needed for Lending Protocol are included, // even if they are set to unsupported. + FeatureBitset const all_{jtx::testableAmendments()}; + // All 2^N permutations of `all_` with each subset of the given features + // excluded. The first entry is always `all_` itself (empty exclusion); + // the last excludes every feature in the list. + std::vector + amendmentCombinations(std::initializer_list features) const + { + std::vector result{all_}; + for (auto const& f : features) + { + auto const n = result.size(); + for (std::size_t i = 0; i < n; ++i) + result.push_back(result[i] - f); + } + return result; + } + std::string const iouCurrency_{"IOU"}; void @@ -1201,7 +1219,8 @@ protected: runLoan( AssetType assetType, BrokerParameters const& brokerParams, - LoanParameters const& loanParams) + LoanParameters const& loanParams, + FeatureBitset features) { using namespace jtx; @@ -1209,7 +1228,7 @@ protected: Account const lender("lender"); Account const borrower("borrower"); - Env env(*this, all_); + Env env(*this, features); auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); @@ -2896,7 +2915,7 @@ protected: } void - testLoanSet() + testLoanSet(FeatureBitset features) { using namespace jtx; @@ -2915,7 +2934,7 @@ protected: std::function mptTest, std::function iouTest, CaseArgs args = {}) { - Env env(*this, all_); + Env env(*this, features); env.fund(XRP(args.initialXRP), issuer, lender, borrower); env.close(); if (args.requireAuth) @@ -3453,14 +3472,14 @@ protected: } void - testLifecycle() + testLifecycle(FeatureBitset features) { testcase("Lifecycle"); using namespace jtx; // Create 3 loan brokers: one for XRP, one for an IOU, and one for // an MPT. That'll require three corresponding SAVs. - Env env(*this, all_); + Env env(*this, features); Account const issuer{"issuer"}; // For simplicity, lender will be the sole actor for the vault & @@ -3547,7 +3566,7 @@ protected: } void - testSelfLoan() + testSelfLoan(FeatureBitset features) { testcase << "Self Loan"; @@ -3555,7 +3574,7 @@ protected: using namespace std::chrono_literals; // Create 3 loan brokers: one for XRP, one for an IOU, and one for // an MPT. That'll require three corresponding SAVs. - Env env(*this, all_); + Env env(*this, features); Account const issuer{"issuer"}; // For simplicity, lender will be the sole actor for the vault & @@ -3682,7 +3701,7 @@ protected: } void - testBatchBypassCounterparty() + testBatchBypassCounterparty(FeatureBitset features) { // From FIND-001 testcase << "Batch Bypass Counterparty"; @@ -3693,7 +3712,7 @@ protected: using namespace jtx; using namespace std::chrono_literals; - Env env(*this, all_); + Env env(*this, features); Account const lender{"lender"}; Account const borrower{"borrower"}; @@ -3749,14 +3768,14 @@ protected: } void - testWrongMaxDebtBehavior() + testWrongMaxDebtBehavior(FeatureBitset features) { // From FIND-003 testcase << "Wrong Max Debt Behavior"; using namespace jtx; using namespace std::chrono_literals; - Env env(*this, all_); + Env env(*this, features); Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -3795,7 +3814,7 @@ protected: } void - testLoanPayComputePeriodicPaymentValidRateInvariant() + testLoanPayComputePeriodicPaymentValidRateInvariant(FeatureBitset features) { // From FIND-012 testcase << "LoanPay xrpl::detail::computePeriodicPayment : " @@ -3803,7 +3822,7 @@ protected: using namespace jtx; using namespace std::chrono_literals; - Env env(*this, all_); + Env env(*this, features); Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -3863,7 +3882,7 @@ protected: } void - testRPC() + testRPC(FeatureBitset features) { // This will expand as more test cases are added. Some functionality // is tested in other test functions. @@ -3871,7 +3890,7 @@ protected: using namespace jtx; - Env env(*this, all_); + Env env(*this, features); auto lowerFee = [&]() { // Run the local fee back down. @@ -4542,7 +4561,7 @@ protected: } void - testAccountSendMptMinAmountInvariant() + testAccountSendMptMinAmountInvariant(FeatureBitset features) { // (From FIND-006) testcase << "LoanSet trigger xrpl::accountSendMPT : minimum amount " @@ -4550,7 +4569,7 @@ protected: using namespace jtx; using namespace std::chrono_literals; - Env env(*this, all_); + Env env(*this, features); Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -4602,7 +4621,7 @@ protected: } void - testLoanPayDebtDecreaseInvariant() + testLoanPayDebtDecreaseInvariant(FeatureBitset features) { // From FIND-007 testcase << "LoanPay xrpl::LoanPay::doApply : debtDecrease " @@ -4611,7 +4630,7 @@ protected: using namespace jtx; using namespace std::chrono_literals; using namespace Lending; - Env env(*this, all_); + Env env(*this, features); Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -4695,14 +4714,14 @@ protected: } void - testLoanPayComputePeriodicPaymentValidTotalInterestInvariant() + testLoanPayComputePeriodicPaymentValidTotalInterestInvariant(FeatureBitset features) { // From FIND-010 testcase << "xrpl::loanComputePaymentParts : valid total interest"; using namespace jtx; using namespace std::chrono_literals; - Env env(*this, all_); + Env env(*this, features); Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -4902,7 +4921,7 @@ protected: } void - testLoanPayComputePeriodicPaymentValidTotalPrincipalPaidInvariant() + testLoanPayComputePeriodicPaymentValidTotalPrincipalPaidInvariant(FeatureBitset features) { // From FIND-009 testcase << "xrpl::loanComputePaymentParts : totalPrincipalPaid " @@ -4911,7 +4930,7 @@ protected: using namespace jtx; using namespace std::chrono_literals; using namespace Lending; - Env env(*this, all_); + Env env(*this, features); Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -5004,7 +5023,7 @@ protected: } void - testLoanPayComputePeriodicPaymentValidTotalInterestPaidInvariant() + testLoanPayComputePeriodicPaymentValidTotalInterestPaidInvariant(FeatureBitset features) { // From FIND-008 testcase << "xrpl::loanComputePaymentParts : loanValueChange rounded"; @@ -5012,7 +5031,7 @@ protected: using namespace jtx; using namespace std::chrono_literals; using namespace Lending; - Env env(*this, all_); + Env env(*this, features); Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -5089,7 +5108,7 @@ protected: } void - testLoanNextPaymentDueDateOverflow() + testLoanNextPaymentDueDateOverflow(FeatureBitset features) { // For FIND-013 testcase << "Prevent nextPaymentDueDate overflow"; @@ -5097,7 +5116,7 @@ protected: using namespace jtx; using namespace std::chrono_literals; using namespace Lending; - Env env(*this, all_); + Env env{*this, features}; Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -5621,14 +5640,14 @@ protected: #if LOAN_TODO void - testLoanPayLateFullPaymentBypassesPenalties() + testLoanPayLateFullPaymentBypassesPenalties(FeatureBitset features) { testcase("LoanPay full payment skips late penalties"); using namespace jtx; using namespace loan; using namespace std::chrono_literals; - Env env(*this, all); + Env env(*this, features); Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -5774,7 +5793,7 @@ protected: } void - testLoanCoverMinimumRoundingExploit() + testLoanCoverMinimumRoundingExploit(FeatureBitset features) { auto testLoanCoverMinimumRoundingExploit = [&, this](Number const& principalRequest) { testcase << "LoanBrokerCoverClawback drains cover via rounding" @@ -5784,7 +5803,7 @@ protected: using namespace loan; using namespace loanBroker; - Env env(*this, all); + Env env(*this, features); Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -5860,7 +5879,7 @@ protected: #endif void - testPoCUnsignedUnderflowOnFullPayAfterEarlyPeriodic() + testPoCUnsignedUnderflowOnFullPayAfterEarlyPeriodic(FeatureBitset features) { // --- PoC Summary ---------------------------------------------------- // Scenario: Borrower makes one periodic payment early (before next due) @@ -5882,7 +5901,7 @@ protected: using namespace loan; using namespace std::chrono_literals; - Env env(*this, all_); + Env env{*this, features}; Account const lender{"poc_lender4"}; Account const borrower{"poc_borrower4"}; @@ -6085,13 +6104,13 @@ protected: } void - testDustManipulation() + testDustManipulation(FeatureBitset features) { testcase("Dust manipulation"); using namespace jtx; using namespace std::chrono_literals; - Env env(*this, all_); + Env env{*this, features}; // Setup: Create accounts Account const issuer{"issuer"}; @@ -6225,7 +6244,7 @@ protected: } void - testRIPD3831() + testRIPD3831(FeatureBitset features) { using namespace jtx; @@ -6252,7 +6271,7 @@ protected: auto const assetType = AssetType::XRP; - Env env(*this, all_); + Env env{*this, features}; auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); @@ -6298,7 +6317,7 @@ protected: } void - testRIPD3459() + testRIPD3459(FeatureBitset features) { testcase("RIPD-3459 - LoanBroker incorrect debt total"); @@ -6323,7 +6342,7 @@ protected: auto const assetType = AssetType::MPT; - Env env(*this, all_); + Env env{*this, features}; auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); @@ -6423,14 +6442,14 @@ protected: } void - testRoundingAllowsUndercoverage() + testRoundingAllowsUndercoverage(FeatureBitset features) { testcase("Minimum cover rounding allows undercoverage (XRP)"); using namespace jtx; using namespace loanBroker; - Env env(*this, all_); + Env env{*this, features}; Account const lender{"lender"}; Account const borrower{"borrower"}; @@ -6502,7 +6521,7 @@ protected: } void - testRIPD3902() + testRIPD3902(FeatureBitset features) { testcase("RIPD-3902 - 1 IOU loan payments"); @@ -6529,7 +6548,7 @@ protected: auto const assetType = AssetType::IOU; - Env env(*this, all_); + Env env{*this, features}; auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower); @@ -6673,7 +6692,7 @@ protected: } void - testIssuerIsBorrower() + testIssuerIsBorrower(FeatureBitset features) { testcase("RIPD-4096 - Issuer as borrower"); @@ -6693,7 +6712,7 @@ protected: auto const assetType = AssetType::IOU; - Env env(*this, all_); + Env env{*this, features}; auto loanResult = createLoan(env, assetType, brokerParams, loanParams, issuer, lender, issuer); @@ -6790,14 +6809,14 @@ protected: } void - testOverpaymentManagementFee() + testOverpaymentManagementFee(FeatureBitset features) { testcase("testOverpaymentManagementFee"); using namespace jtx; using namespace loan; - Env env(*this, all_); + Env env{*this, features}; Account const lender{"lender"}, borrower{"borrower"}; @@ -6843,7 +6862,7 @@ protected: } void - testLoanPayBrokerOwnerMissingTrustline() + testLoanPayBrokerOwnerMissingTrustline(FeatureBitset features) { testcase << "LoanPay Broker Owner Missing Trustline (PoC)"; using namespace jtx; @@ -6852,7 +6871,7 @@ protected: Account const borrower("borrower"); Account const broker("broker"); auto const iou = issuer["IOU"]; - Env env(*this, all_); + Env env(*this, features); env.fund(XRP(20'000), issuer, broker, borrower); env.close(); // Set up trustlines and fund accounts @@ -6911,7 +6930,7 @@ protected: } void - testLoanPayBrokerOwnerUnauthorizedMPT() + testLoanPayBrokerOwnerUnauthorizedMPT(FeatureBitset features) { testcase << "LoanPay Broker Owner MPT unauthorized"; using namespace jtx; @@ -6921,7 +6940,7 @@ protected: Account const borrower("borrower"); Account const broker("broker"); - Env env(*this, all_); + Env env{*this, features}; env.fund(XRP(20'000), issuer, broker, borrower); env.close(); @@ -6992,7 +7011,7 @@ protected: } void - testLoanPayBrokerOwnerNoPermissionedDomainMPT() + testLoanPayBrokerOwnerNoPermissionedDomainMPT(FeatureBitset features) { testcase << "LoanPay Broker Owner without permissioned domain of the MPT"; using namespace jtx; @@ -7002,7 +7021,7 @@ protected: Account const borrower("borrower"); Account const broker("broker"); - Env env(*this, all_); + Env env{*this, features}; env.fund(XRP(20'000), issuer, broker, borrower); env.close(); @@ -7095,7 +7114,7 @@ protected: } void - testLoanSetBrokerOwnerNoPermissionedDomainMPT() + testLoanSetBrokerOwnerNoPermissionedDomainMPT(FeatureBitset features) { testcase << "LoanSet Broker Owner without permissioned domain of the MPT"; using namespace jtx; @@ -7105,7 +7124,7 @@ protected: Account const borrower("borrower"); Account const broker("broker"); - Env env(*this, all_); + Env env{*this, features}; env.fund(XRP(20'000), issuer, broker, borrower); env.close(); @@ -7167,7 +7186,7 @@ protected: } void - testSequentialFLCDepletion() + testSequentialFLCDepletion(FeatureBitset features) { testcase << "First-Loss Capital Depletion on Sequential Defaults"; @@ -7175,7 +7194,7 @@ protected: using namespace loan; using namespace loanBroker; - Env env(*this, all_); + Env env{*this, features}; Account const issuer{"issuer"}; Account const lender{"lender"}; @@ -7427,7 +7446,7 @@ protected: // loss from an impaired loan, ensuring the invariant check properly // accounts for the loss. void - testWithdrawReflectsUnrealizedLoss() + testWithdrawReflectsUnrealizedLoss(FeatureBitset features) { using namespace jtx; using namespace loan; @@ -7446,7 +7465,7 @@ protected: static constexpr std::uint32_t kLocalPaymentInterval = 600; static constexpr std::uint32_t kLocalPaymentTotal = 2; - Env env(*this, all_); + Env env{*this, features}; // Setup accounts Account const issuer{"issuer"}; @@ -7553,6 +7572,110 @@ protected: attemptWithdrawShares(depositorB, sharesLpB, tesSUCCESS); } + // Regression for the dual-rounding fix at coarse (integer-MPT) scale. + // + // Loan: P=1, r=50% (50000 tenth-bips), n=3, yearly interval. The + // amortization schedule produces a fractional principal + // (~0.47) which under round-to-nearest collapses to 0 in a single + // step, causing `doPayment`'s strict `>` assertion on principal to + // fire mid-loan. With fixCleanup3_2_0 enabled, principal is rounded + // upward (sticks at 1 across the first two periods) and only clears + // in the final payment. + // + // The test pays one period at a time across three LoanPay + // transactions and verifies the loan completes (paymentRemaining=0) + // with totals matching the loan's economics (1 principal + 2 interest). + void + testIntegerScalePrincipalSticks(FeatureBitset features) + { + // Without fixCleanup3_2_0, this behavior will abort the server, so + // don't run without it. + if (!features[fixCleanup3_2_0]) + return; + + testcase("edge: integer MPT principal stuck mid-loan completes via final"); + + using namespace jtx; + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const lender{"lender"}; + Account const borrower{"borrower"}; + + env.fund(XRP(100'000), issuer, lender, borrower); + env.close(); + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.maxAmt = 100'000, .flags = tfMPTCanTransfer}); + PrettyAsset const asset{mptt.issuanceID()}; + + mptt.authorize({.account = lender}); + mptt.authorize({.account = borrower}); + + env(pay(issuer, lender, asset(10'000))); + env(pay(issuer, borrower, asset(10'000))); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = lender, .asset = asset}); + env(vaultTx); + env.close(); + + env(vault.deposit({.depositor = lender, .id = vaultKeylet.key, .amount = asset(5'000)})); + env.close(); + + auto const brokerKeylet = keylet::loanbroker(lender.id(), env.seq(lender)); + env(loanBroker::set(lender, vaultKeylet.key), + loanBroker::kDebtMaximum(Number{100}), + Fee(env.current()->fees().base * 2)); + env.close(); + + auto const brokerStateBefore = env.le(brokerKeylet); + if (!BEAST_EXPECT(brokerStateBefore)) + return; + auto const loanSequence = brokerStateBefore->at(sfLoanSequence); + auto const loanKeylet = keylet::loan(brokerKeylet.key, loanSequence); + + env(loan::set(borrower, brokerKeylet.key, Number{1}), + Sig(sfCounterpartySignature, lender), + loan::kInterestRate(TenthBips32{50'000}), + loan::kPaymentTotal(3), + loan::kPaymentInterval(31'536'000), + Fee(env.current()->fees().base * 2)); + env.close(); + + auto const borrowerStart = env.balance(borrower, asset).value(); + + // Three separate periodic payments of 1 each. Expected per-period + // evolution at integer MPT scale (TVO = PO + interestDue + + // managementFeeDue): + // start: PO=1, TVO=3, paymentRemaining=3 + // after pay #1: PO=1, TVO=2, paymentRemaining=2 (principal sticks) + // after pay #2: PO=1, TVO=1, paymentRemaining=1 (principal sticks) + // after pay #3: PO=0, TVO=0, paymentRemaining=0 (final clears) + std::array const expectedPO{Number{1}, Number{1}, Number{0}}; + std::array const expectedTVO{Number{2}, Number{1}, Number{0}}; + std::array const expectedRemaining{2, 1, 0}; + + for (int i = 0; i < 3; ++i) + { + env(loan::pay(borrower, loanKeylet.key, asset(1)), Ter(tesSUCCESS)); + env.close(); + + auto const sle = env.le(loanKeylet); + if (!BEAST_EXPECT(sle)) + return; + BEAST_EXPECT(sle->at(sfPrincipalOutstanding) == expectedPO[i]); + BEAST_EXPECT(sle->at(sfTotalValueOutstanding) == expectedTVO[i]); + BEAST_EXPECT(sle->at(sfPaymentRemaining) == expectedRemaining[i]); + } + + // Borrower paid 3 total regardless of fee split (1 principal + 2 + // interest+fee, matching loan economics). + auto const borrowerEnd = env.balance(borrower, asset).value(); + BEAST_EXPECT(borrowerStart - borrowerEnd == asset(3).value()); + } + // A near-zero interest rate on a 100 USD loan // produces total interest of ~6 units at loanScale -9. Numerical error // in the amortization formula pushes the theoretical principal above @@ -7735,74 +7858,91 @@ protected: to_string(tolerance)); } + void + runAmendmentIndependent() + { + testDisabled(); + testInvalidLoanSet(); + testInvalidLoanDelete(); + testInvalidLoanManage(); + testInvalidLoanPay(); + testIssuerLoan(); + testServiceFeeOnBrokerDeepFreeze(); + testRequireAuth(); + testRIPD3901(); + testBorrowerIsBroker(); + testLimitExceeded(); + + for (auto const flags : {0u, tfLoanOverpayment}) + testYieldTheftRounding(flags); + testBugInterestDueDeltaCrash(); + testFullLifecycleVaultPnLNearZeroRate(); + } + + // Tests run under each entry in amendmentCombinations(). + void + runAmendmentSensitive(FeatureBitset features) + { +#if LOAN_TODO + testLoanPayLateFullPaymentBypassesPenalties(features); + testLoanCoverMinimumRoundingExploit(features); +#endif + + // Lifecycle + testSelfLoan(features); + testLoanSet(features); + testLifecycle(features); + + // Payment paths + testWithdrawReflectsUnrealizedLoss(features); + testPoCUnsignedUnderflowOnFullPayAfterEarlyPeriodic(features); + testBatchBypassCounterparty(features); + testLoanNextPaymentDueDateOverflow(features); + testCoverDepositWithdrawNonTransferableMPT(features); + testSequentialFLCDepletion(features); + + // Invariants + testLoanPayComputePeriodicPaymentValidRateInvariant(features); + testAccountSendMptMinAmountInvariant(features); + testLoanPayDebtDecreaseInvariant(features); + testWrongMaxDebtBehavior(features); + testLoanPayComputePeriodicPaymentValidTotalInterestInvariant(features); + testLoanPayComputePeriodicPaymentValidTotalPrincipalPaidInvariant(features); + testLoanPayComputePeriodicPaymentValidTotalInterestPaidInvariant(features); + + // RPC + testRPC(features); + + // Edge / rounding + testDustManipulation(features); + testRoundingAllowsUndercoverage(features); + testOverpaymentManagementFee(features); + testIssuerIsBorrower(features); + testIntegerScalePrincipalSticks(features); + + // RIPD regressions + testRIPD3831(features); + testRIPD3459(features); + testRIPD3902(features); + + // Broker-owner permissions + testLoanPayBrokerOwnerMissingTrustline(features); + testLoanPayBrokerOwnerUnauthorizedMPT(features); + testLoanPayBrokerOwnerNoPermissionedDomainMPT(features); + testLoanSetBrokerOwnerNoPermissionedDomainMPT(features); + } + public: void run() override { -#if LOAN_TODO - testLoanPayLateFullPaymentBypassesPenalties(); - testLoanCoverMinimumRoundingExploit(); -#endif - for (auto const flags : {0u, tfLoanOverpayment}) - { - testYieldTheftRounding(flags); - } - - testBugInterestDueDeltaCrash(); - testFullLifecycleVaultPnLNearZeroRate(); - - testWithdrawReflectsUnrealizedLoss(); - testInvalidLoanSet(); - - auto const all = jtx::testableAmendments(); - testCoverDepositWithdrawNonTransferableMPT(all); - testCoverDepositWithdrawNonTransferableMPT(all - featureMPTokensV2); - testCoverDepositWithdrawNonTransferableMPT(all - fixCleanup3_2_0); + runAmendmentIndependent(); testLoanSetBlockedLoanPayAllowedWhenCanTransferCleared(); testLendingCanTradeClearedNoImpact(); - testPoCUnsignedUnderflowOnFullPayAfterEarlyPeriodic(); - - testDisabled(); - testSelfLoan(); - testIssuerLoan(); - testLoanSet(); - testLifecycle(); - testServiceFeeOnBrokerDeepFreeze(); - - testRPC(); - testInvalidLoanDelete(); - testInvalidLoanManage(); - testInvalidLoanPay(); - - testBatchBypassCounterparty(); - testLoanPayComputePeriodicPaymentValidRateInvariant(); - testAccountSendMptMinAmountInvariant(); - testLoanPayDebtDecreaseInvariant(); - testWrongMaxDebtBehavior(); - testLoanPayComputePeriodicPaymentValidTotalInterestInvariant(); - testDosLoanPay(all | fixCleanup3_1_3); - testDosLoanPay(all - fixCleanup3_1_3); - testLoanPayComputePeriodicPaymentValidTotalPrincipalPaidInvariant(); - testLoanPayComputePeriodicPaymentValidTotalInterestPaidInvariant(); - testLoanNextPaymentDueDateOverflow(); - - testRequireAuth(); - testDustManipulation(); - - testRIPD3831(); - testRIPD3459(); - testRIPD3901(); - testRIPD3902(); - testRoundingAllowsUndercoverage(); - testBorrowerIsBroker(); - testIssuerIsBorrower(); - testLimitExceeded(); - testOverpaymentManagementFee(); - testLoanPayBrokerOwnerMissingTrustline(); - testLoanPayBrokerOwnerUnauthorizedMPT(); - testLoanPayBrokerOwnerNoPermissionedDomainMPT(); - testLoanSetBrokerOwnerNoPermissionedDomainMPT(); - testSequentialFLCDepletion(); + testDosLoanPay(all_ | fixCleanup3_1_3); + testDosLoanPay(all_ - fixCleanup3_1_3); + for (auto const& features : amendmentCombinations({fixCleanup3_2_0, featureMPTokensV2})) + runAmendmentSensitive(features); } }; @@ -7867,7 +8007,7 @@ protected: .payInterval = payInterval, }; - runLoan(assetType, brokerParams, loanParams); + runLoan(assetType, brokerParams, loanParams, all_); } public: @@ -7926,7 +8066,7 @@ class LoanArbitrary_test : public LoanBatch_test .payTotal = 2, .payInterval = 200}; - runLoan(AssetType::XRP, brokerParams, loanParams); + runLoan(AssetType::XRP, brokerParams, loanParams, all_); } }; From 7fdaa0a5efdd3a11615790cb5c14af50887969d2 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Thu, 21 May 2026 16:51:58 +0200 Subject: [PATCH 11/87] fix: Fix IOU precision issues in LoanBrokerCover transactions (#7274) --- include/xrpl/ledger/helpers/LendingHelpers.h | 30 +++ include/xrpl/protocol/STAmount.h | 18 ++ src/libxrpl/ledger/helpers/LendingHelpers.cpp | 33 +++ src/libxrpl/protocol/STAmount.cpp | 5 + .../lending/LoanBrokerCoverClawback.cpp | 4 + .../lending/LoanBrokerCoverDeposit.cpp | 54 ++++- .../lending/LoanBrokerCoverWithdraw.cpp | 5 + src/test/app/LendingHelpers_test.cpp | 101 ++++++++- src/test/app/LoanBroker_test.cpp | 212 ++++++++++++++++++ src/test/protocol/STAmount_test.cpp | 95 ++++++++ 10 files changed, 546 insertions(+), 11 deletions(-) diff --git a/include/xrpl/ledger/helpers/LendingHelpers.h b/include/xrpl/ledger/helpers/LendingHelpers.h index a6ab42254b..cce41a38c5 100644 --- a/include/xrpl/ledger/helpers/LendingHelpers.h +++ b/include/xrpl/ledger/helpers/LendingHelpers.h @@ -4,8 +4,38 @@ #include #include +#include + namespace xrpl { +/** + * Broker cover preclaim precision guard (fixCleanup3_2_0). + * + * Prevents a "silent sub-ULP no-op" where a deposit, withdrawal, or clawback + * amount is so small that it rounds to zero at `sfCoverAvailable`'s scale. + * Without this guard, both the pseudo trust-line and `sfCoverAvailable` would + * identically absorb the rounded zero, resulting in a successful transaction + * (tesSUCCESS) where no funds actually moved. + * + * @param view Read view (rules used for amendment gating). + * @param sleBroker The loan broker SLE (read-only). + * @param vaultAsset The underlying vault asset (the broker's cover asset). + * @param amount The effective subtraction/addition amount. + * @param j Journal for logging. + * @param logPrefix Transactor name for log diagnostics. + * + * @return `tecPRECISION_LOSS` if the request rounds to zero at cover scale. + * `tesSUCCESS` if the amendment is disabled or the request is safely supra-ULP. + */ +[[nodiscard]] TER +canApplyToBrokerCover( + ReadView const& view, + SLE::const_ref sleBroker, + Asset const& vaultAsset, + STAmount const& amount, + beast::Journal j, + std::string_view logPrefix); + // Lending protocol has dependencies, so capture them here. bool checkLendingProtocolDependencies(Rules const& rules, STTx const& tx); diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index 01511c23ea..c576c0da31 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -184,6 +184,24 @@ public: [[nodiscard]] STAmount const& value() const noexcept; + /** + * Checks if this amount evaluates to zero when constrained to a specific + * accounting scale. + * + * For XRP and MPT `roundToScale` is a no-op, returns true only when the amount itself is zero. + * The `scale` argument is ignored in that case. + * For IOU, the amount is rounded to the given scale using Number::RoundingMode::ToNearest mode + * and the result is checked for zero; if `scale <= exponent()`, `roundToScale` short-circuits + * and returns the value unchanged, so this returns false for any non-zero amount. + * + * @param scale The target accounting scale to evaluate against. + * @return `true` if this amount rounds to zero at the given scale, `false` otherwise. + * + * @see roundToScale + */ + [[nodiscard]] bool + isZeroAtScale(int scale) const; + //-------------------------------------------------------------------------- // // Operators diff --git a/src/libxrpl/ledger/helpers/LendingHelpers.cpp b/src/libxrpl/ledger/helpers/LendingHelpers.cpp index 2c756c2877..1fedbb5f13 100644 --- a/src/libxrpl/ledger/helpers/LendingHelpers.cpp +++ b/src/libxrpl/ledger/helpers/LendingHelpers.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -24,10 +25,42 @@ #include #include #include +#include #include namespace xrpl { +[[nodiscard]] TER +canApplyToBrokerCover( + ReadView const& view, + SLE::const_ref sleBroker, + Asset const& vaultAsset, + STAmount const& amount, + beast::Journal j, + std::string_view logPrefix) +{ + XRPL_ASSERT( + sleBroker && sleBroker->getType() == ltLOAN_BROKER, + "xrpl::canApplyToBrokerCover : valid LoanBroker sle"); + XRPL_ASSERT(vaultAsset == amount.asset(), "xrpl::canApplyToBrokerCover : valid asset"); + + if (!view.rules().enabled(fixCleanup3_2_0)) + return tesSUCCESS; + + if (amount == beast::kZero) + return tecPRECISION_LOSS; + + int const coverScale = scale(sleBroker->at(sfCoverAvailable), vaultAsset); + if (amount.isZeroAtScale(coverScale)) + { + JLOG(j.warn()) << logPrefix << ": amount " << amount.getFullText() + << " rounds to zero at cover scale " << coverScale; + return tecPRECISION_LOSS; + } + + return tesSUCCESS; +} + bool checkLendingProtocolDependencies(Rules const& rules, STTx const& tx) { diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 2d051722bf..c40beabf12 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -1738,4 +1738,9 @@ divRoundStrict(STAmount const& num, STAmount const& den, Asset const& asset, boo return divRoundImpl(num, den, asset, roundUp); } +[[nodiscard]] bool +STAmount::isZeroAtScale(int scale) const +{ + return roundToScale(*this, scale, Number::RoundingMode::ToNearest).signum() == 0; +} } // namespace xrpl diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp index ab26353a1c..48cb6b90aa 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp @@ -291,6 +291,10 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx) } STAmount const& clawAmount = *findClawAmount; + if (auto const ret = canApplyToBrokerCover( + ctx.view, sleBroker, vaultAsset, clawAmount, ctx.j, "LoanBrokerCoverClawback")) + return ret; + // Explicitly check the balance of the trust line / MPT to make sure the // balance is actually there. It should always match `sfCoverAvailable`, so // if there isn't, this is an internal error. diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp index 04905d5ea3..e84f277f5b 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp @@ -1,9 +1,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -87,6 +89,29 @@ LoanBrokerCoverDeposit::preclaim(PreclaimContext const& ctx) if (auto const ret = requireAuth(ctx.view, vaultAsset, account, AuthType::StrongAuth)) return ret; + // Deposit must round the amount Downward to cover scale and then reuse that rounded + // value for the actual transfer in doApply — otherwise implicit round-to-nearest during + // `sfCoverAvailable +=` could credit the broker more than the depositor paid Computing it + // here in preclaim lets us reject sub-cover-scale dust early with tecPRECISION_LOSS instead of + // failing only in doApply. + bool const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0); + auto const roundedAmount = [&]() -> STAmount { + if (!fix320Enabled) + return tx[sfAmount]; + + return roundToScale( + tx[sfAmount], + scale(sleBroker->at(sfCoverAvailable), vaultAsset), + Number::RoundingMode::Downward); + }(); + + if (fix320Enabled && roundedAmount == beast::kZero) + { + JLOG(ctx.j.warn()) << "LoanBrokerCoverDeposit: deposit amount: " << tx[sfAmount] + << " is zero at loan broker scale"; + return tecPRECISION_LOSS; + } + if (accountHolds( ctx.view, account, @@ -94,7 +119,7 @@ LoanBrokerCoverDeposit::preclaim(PreclaimContext const& ctx) FreezeHandling::ZeroIfFrozen, AuthHandling::ZeroIfUnauthorized, ctx.j, - SpendableHandling::FullBalance) < amount) + SpendableHandling::FullBalance) < roundedAmount) return tecINSUFFICIENT_FUNDS; return tesSUCCESS; @@ -106,8 +131,6 @@ LoanBrokerCoverDeposit::doApply() auto const& tx = ctx_.tx; auto const brokerID = tx[sfLoanBrokerID]; - auto const amount = tx[sfAmount]; - auto broker = view().peek(keylet::loanbroker(brokerID)); if (!broker) return tecINTERNAL; // LCOV_EXCL_LINE @@ -117,9 +140,32 @@ LoanBrokerCoverDeposit::doApply() return tecINTERNAL; // LCOV_EXCL_LINE auto const vaultAsset = vault->at(sfAsset); - auto const brokerPseudoID = broker->at(sfAccount); + // Re-round here (matches preclaim) so the same cover-scale-quantized + // value drives both the trustline transfer and the cover increment; + // see the rationale comment in preclaim. + bool const fix320Enabled = view().rules().enabled(fixCleanup3_2_0); + auto const amount = [&]() -> STAmount { + if (!fix320Enabled) + return tx[sfAmount]; + + return roundToScale( + tx[sfAmount], + scale(broker->at(sfCoverAvailable), vaultAsset), + Number::RoundingMode::Downward); + }(); + + // We validated zero-amount in preclaim, if we ended up with zero now, fail hard. + if (amount == beast::kZero) + { + // LCOV_EXCL_START + JLOG(j_.error()) << "LoanBrokerCoverDeposit: deposit amount: " << tx[sfAmount] + << " is zero"; + return tecINTERNAL; + // LCOV_EXCL_STOP + } + // Transfer assets from depositor to pseudo-account. if (auto ter = accountSend(view(), accountID_, brokerPseudoID, amount, j_, WaiveTransferFee::Yes)) diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp index 331c44b1e8..dbe2de2100 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp @@ -94,6 +94,11 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx) if (amount.asset() != vaultAsset) return tecWRONG_ASSET; + // Helper handles both IOU and MPT correctly without explicit branching. + if (auto const ret = canApplyToBrokerCover( + ctx.view, sleBroker, vaultAsset, amount, ctx.j, "LoanBrokerCoverWithdraw")) + return ret; + // The broker's pseudo-account is the source of funds. auto const pseudoAccountID = sleBroker->at(sfAccount); // Post-fixCleanup3_2_0: cover withdraw is a recovery path that bypasses diff --git a/src/test/app/LendingHelpers_test.cpp b/src/test/app/LendingHelpers_test.cpp index 96d0722732..af46dd2e0f 100644 --- a/src/test/app/LendingHelpers_test.cpp +++ b/src/test/app/LendingHelpers_test.cpp @@ -8,9 +8,15 @@ #include #include #include +#include +#include +#include +#include +#include #include #include +#include #include #include @@ -287,9 +293,9 @@ class LendingHelpers_test : public beast::unit_test::Suite std::uint32_t n; }; auto const cases = std::vector{ - {"r=5%, n=3", Number{5, -2}, 3}, - {"r=0.1%, n=1000", Number{1, -3}, 1'000}, - {"r=1e-7, n=100 (above threshold by 10x)", Number{1, -7}, 100}, + {.name = "r=5%, n=3", .r = Number{5, -2}, .n = 3}, + {.name = "r=0.1%, n=1000", .r = Number{1, -3}, .n = 1'000}, + {.name = "r=1e-7, n=100 (above threshold by 10x)", .r = Number{1, -7}, .n = 100}, }; for (auto const& tc : cases) { @@ -318,8 +324,10 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const cases = std::vector{ // bug regime: r = 1 TenthBips32 over 600s payment interval // → r ≈ 1.9e-10, r*n ≈ 3.8e-10 < 1e-9. - {"bug regime: r~1.9e-10, n=2", loanPeriodicRate(TenthBips32{1}, 600), 2}, - {"r=1e-12, n=100", Number{1, -12}, 100}, + {.name = "bug regime: r~1.9e-10, n=2", + .r = loanPeriodicRate(TenthBips32{1}, 600), + .n = 2}, + {.name = "r=1e-12, n=100", .r = Number{1, -12}, .n = 100}, }; for (auto const& tc : cases) { @@ -356,8 +364,8 @@ class LendingHelpers_test : public beast::unit_test::Suite std::uint32_t n; }; auto const cases = std::vector{ - {"r=1e-9, n=1", Number{1, -9}, 1}, - {"r=1e-12, n=1000", Number{1, -12}, 1'000}, + {.name = "r=1e-9, n=1", .r = Number{1, -9}, .n = 1}, + {.name = "r=1e-12, n=1000", .r = Number{1, -12}, .n = 1'000}, }; for (auto const& tc : cases) @@ -1439,6 +1447,84 @@ class LendingHelpers_test : public beast::unit_test::Suite } public: + void + testCanApplyToBrokerCover() + { + using namespace jtx; + + Account const issuer{"issuer"}; + PrettyAsset const iou = issuer["IOU"]; + + // sfCoverAvailable = Number{10} on an IOU → STAmount exponent = -14, + // so coverScale = -14. The ULP boundary is 5e-15; anything below + // that rounds to zero at cover scale. Number{1,-16} = 1e-16 is our + // representative sub-ULP probe. + struct TestCase + { + std::string name; + Number coverAvailable; + STAmount amount; + TER expected; + }; + + auto const testCases = std::vector{ + { + .name = "Zero amount", + .coverAvailable = Number{10}, + .amount = STAmount{iou, Number{0}}, + .expected = tecPRECISION_LOSS, + }, + { + .name = "Rounds to zero at cover scale", + .coverAvailable = Number{10}, + .amount = STAmount{iou, Number{1, -16}}, + .expected = tecPRECISION_LOSS, + }, + { + .name = "Zero coverAvailable, whole-unit amount", + // coverScale = 0 (zero STAmount exponent); 1 IOU is not + // zero at integer scale → tesSUCCESS. + .coverAvailable = Number{0}, + .amount = STAmount{iou, Number{1}}, + .expected = tesSUCCESS, + }, + { + .name = "Supra-ULP amount", + .coverAvailable = Number{10}, + .amount = STAmount{iou, Number{1, -13}}, + .expected = tesSUCCESS, + }, + }; + + Env const env{*this}; + + for (auto const& tc : testCases) + { + testcase("canApplyToBrokerCover: " + tc.name); + auto sle = std::make_shared(ltLOAN_BROKER, uint256{1u}); + sle->at(sfCoverAvailable) = tc.coverAvailable; + BEAST_EXPECT( + canApplyToBrokerCover(*env.current(), sle, iou, tc.amount, env.journal, "test") == + tc.expected); + } + + // Amendment off → guard is bypassed regardless of amount. + { + testcase("canApplyToBrokerCover: amendment disabled"); + Env const envOff{*this, testableAmendments() - fixCleanup3_2_0}; + auto sle = std::make_shared(ltLOAN_BROKER, uint256{1u}); + sle->at(sfCoverAvailable) = Number{10}; + BEAST_EXPECT( + canApplyToBrokerCover( + *envOff.current(), + sle, + iou, + STAmount{iou, Number{0}}, + envOff.journal, + "test") == tesSUCCESS); + } + } + void run() override { @@ -1462,6 +1548,7 @@ public: testComputePaymentFactorNearZeroRate(); testComputeOverpaymentComponents(); testComputeInterestAndFeeParts(); + testCanApplyToBrokerCover(); } }; diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index a7e036a621..c899778391 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include namespace xrpl::test { @@ -1823,10 +1824,221 @@ class LoanBroker_test : public beast::unit_test::Suite testRIPD4274MPT(); } + // Exercises canApplyToBrokerCover (fixCleanup3_2_0): a deposit, withdraw, + // or clawback whose amount rounds to zero at sfCoverAvailable's precision + // scale must be rejected with tecPRECISION_LOSS once the amendment is on, + // and must silently succeed without changing sfCoverAvailable when off. + void + testCoverPrecisionGuard() + { + using namespace jtx; + using namespace loanBroker; + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + + // sfCoverAvailable = 10 IOU → STAmount exponent = -14. + // Anything < 5e-15 rounds to zero at that scale. + // 1e-16 is the representative sub-ULP probe amount. + + // Shared setup: funds accounts, creates a vault + broker with 10 IOU + // cover, and returns {brokerKeylet, iou}. + auto const setup = [&](Env& env) -> std::pair { + Vault const vault{env}; + + env.fund(XRP(100'000), issuer, alice); + env.close(); + env(fset(issuer, asfAllowTrustLineClawback)); + env.close(); + + PrettyAsset const iou = issuer["IOU"]; + env(trust(alice, iou(1'000'000))); + env.close(); + env(pay(issuer, alice, iou(1'000))); + env.close(); + + auto [createTx, vaultKeylet] = vault.create({.owner = alice, .asset = iou}); + env(createTx); + env.close(); + + auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice)); + env(set(alice, vaultKeylet.key)); + env.close(); + + env(coverDeposit(alice, brokerKeylet.key, iou(10))); + env.close(); + + return {brokerKeylet, iou}; + }; + + auto runTestCases = [&](FeatureBitset features) { + TER const expected = + features[fixCleanup3_2_0] ? TER{tecPRECISION_LOSS} : TER{tesSUCCESS}; + + { + testcase("Cover precision guard: Deposit zero-at-scale"); + Env env{*this, features}; + auto const [brokerKeylet, iou] = setup(env); + PrettyAmount const subUlpAmt = iou(Number{1, -16}); + auto const coverBefore = env.le(brokerKeylet)->at(sfCoverAvailable); + env(coverDeposit(alice, brokerKeylet.key, subUlpAmt), Ter(expected)); + env.close(); + if (expected == tesSUCCESS) + { + if (auto const broker = env.le(brokerKeylet); BEAST_EXPECT(broker)) + BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore); + } + } + + { + testcase("Cover precision guard: Deposit rounds down"); + // Both cases succeed; post-fix the amount is rounded DOWN to + // cover scale first, so the delta differs from pre-fix + // Input: 1.8e-14 IOU (sub-scale at cover scale -14) + // Pre-fix: 10 + 1.8e-14 → round-to-nearest → + // 10.00000000000002 → delta 2e-14 + // Post-fix: roundToScale(1.8e-14, -14, Downward) = 1e-14; + // 10 + 1e-14 = 10.00000000000001 → delta 1e-14 + Env env{*this, features}; + auto const [brokerKeylet, iou] = setup(env); + PrettyAmount const subUlpAmt = iou(Number{18, -15}); + auto const coverBefore = env.le(brokerKeylet)->at(sfCoverAvailable); + env(coverDeposit(alice, brokerKeylet.key, subUlpAmt), Ter(tesSUCCESS)); + env.close(); + auto const brokerAfter = env.le(brokerKeylet); + if (!BEAST_EXPECT(brokerAfter)) + return; + + Number const delta = features[fixCleanup3_2_0] ? Number{1, -14} : Number{2, -14}; + BEAST_EXPECT(brokerAfter->at(sfCoverAvailable) - coverBefore == delta); + } + + // Property: post-fix, when the user deposits `x` and cover + // gains `x'`, we always have 0 <= x - x' < 1 ULP at cover + // scale (cover holds 10 IOU → ULP = 1e-14). Pre-fix uses + // STAmount's default round-to-nearest during `+=`, which can + // over-deposit (x' > x), so the property only holds with + // fixCleanup3_2_0 enabled. + if (features[fixCleanup3_2_0]) + { + testcase("Cover precision guard: Deposit rounding bound"); + Env env{*this, features}; + auto const [brokerKeylet, iou] = setup(env); + Number const oneUlp{1, -14}; + // Each requested amount lies strictly between 1·ULP and + // 2·ULP at cover scale; post-fix `roundDown` credits + // exactly `oneUlp` and leaves a strictly-positive, + // strictly-sub-ULP residual. + for (Number const requested : {Number{11, -15}, Number{15, -15}, Number{19, -15}}) + { + auto const broker = env.le(brokerKeylet); + if (!BEAST_EXPECT(broker)) + return; + Number const coverBefore = broker->at(sfCoverAvailable); + env(coverDeposit(alice, brokerKeylet.key, iou(requested)), Ter(tesSUCCESS)); + env.close(); + auto const brokerAfter = env.le(brokerKeylet); + if (!BEAST_EXPECT(brokerAfter)) + return; + Number const coverAfter = brokerAfter->at(sfCoverAvailable); + Number const actual = coverAfter - coverBefore; + Number const lost = requested - actual; + BEAST_EXPECT(lost >= Number{0}); + BEAST_EXPECT(lost < oneUlp); + } + } + + { + testcase("Cover precision guard: Withdraw"); + Env env{*this, features}; + auto const [brokerKeylet, iou] = setup(env); + PrettyAmount const subUlpAmt = iou(Number{1, -16}); + auto const coverBefore = env.le(brokerKeylet)->at(sfCoverAvailable); + auto const aliceBalanceBefore = env.balance(alice, iou); + env(coverWithdraw(alice, brokerKeylet.key, subUlpAmt), Ter(expected)); + env.close(); + if (expected == tesSUCCESS) + { + if (auto const broker = env.le(brokerKeylet); BEAST_EXPECT(broker)) + BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore); + BEAST_EXPECT(env.balance(alice, iou) == aliceBalanceBefore); + } + } + + { + testcase("Cover precision guard: Clawback"); + Env env{*this, features}; + auto const [brokerKeylet, iou] = setup(env); + PrettyAmount const subUlpAmt = iou(Number{1, -16}); + auto const coverBefore = env.le(brokerKeylet)->at(sfCoverAvailable); + env(coverClawback(issuer), + kLoanBrokerId(brokerKeylet.key), + kAmount(subUlpAmt), + Ter(expected)); + env.close(); + if (expected == tesSUCCESS) + { + if (auto const broker = env.le(brokerKeylet); BEAST_EXPECT(broker)) + BEAST_EXPECT(broker->at(sfCoverAvailable) == coverBefore); + } + } + + // MPT amounts are integers; scale is 0; the guard never rejects a + // positive integer amount. Verify all three callsites pass with amendment on. + { + testcase("Cover precision guard: MPT min amount passes"); + Env env{*this, all_}; + + env.fund(XRP(100'000), issuer, alice); + env.close(); + + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); + env.close(); + + PrettyAsset const mptAsset = mptt["MPT"]; + mptt.authorize({.account = alice}); + env.close(); + + env(pay(issuer, alice, mptAsset(100))); + env.close(); + + Vault const vault{env}; + auto [createTx, vaultKeylet] = vault.create({.owner = alice, .asset = mptAsset}); + env(createTx); + env.close(); + + auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice)); + env(set(alice, vaultKeylet.key)); + env.close(); + + env(coverDeposit(alice, brokerKeylet.key, mptAsset(10))); + env.close(); + + env(coverDeposit(alice, brokerKeylet.key, mptAsset(1)), Ter(tesSUCCESS)); + env.close(); + + env(coverWithdraw(alice, brokerKeylet.key, mptAsset(1)), Ter(tesSUCCESS)); + env.close(); + + env(coverClawback(issuer), + kLoanBrokerId(brokerKeylet.key), + kAmount(mptAsset(1)), + Ter(tesSUCCESS)); + env.close(); + } + }; + + runTestCases(all_); + runTestCases(all_ - fixCleanup3_2_0); + } + public: void run() override { + testCoverPrecisionGuard(); + testLoanBrokerSetDebtMaximum(); testLoanBrokerCoverDepositNullVault(); diff --git a/src/test/protocol/STAmount_test.cpp b/src/test/protocol/STAmount_test.cpp index 322d51f84d..c7207589ff 100644 --- a/src/test/protocol/STAmount_test.cpp +++ b/src/test/protocol/STAmount_test.cpp @@ -1205,6 +1205,100 @@ public: //-------------------------------------------------------------------------- + void + testIsZeroAtScale() + { + testcase("isZeroAtScale"); + + Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)}; + + // IOU: 10 IOU — mantissa = kMinValue (10^15), exponent = -14. + // One ULP at this scale is 10^-14; half-ULP is 5*10^-15. + { + STAmount const ref{usd, STAmount::kMinValue, -14}; + int const refScale = ref.exponent(); // -14 + BEAST_EXPECT(refScale == -14); + + // Zero rounds to zero at any scale. + STAmount const iouZero{usd, 0}; + BEAST_EXPECT(iouZero.isZeroAtScale(refScale)); + + // Sub-ULP: 1e-16 IOU (mantissa = kMinValue, exponent = -31). + // Far below half-ULP → rounds to zero. + STAmount const subUlp{usd, STAmount::kMinValue, -31}; + BEAST_EXPECT(subUlp.isZeroAtScale(refScale)); + + // One ULP: 1e-14 IOU (mantissa = kMinValue, exponent = -29). + // Exactly the smallest representable unit at refScale → not zero. + STAmount const oneUlp{usd, STAmount::kMinValue, -29}; + BEAST_EXPECT(!oneUlp.isZeroAtScale(refScale)); + + // The reference value itself: exponent == scale → returned + // unchanged → not zero. + BEAST_EXPECT(!ref.isZeroAtScale(refScale)); + + // A much larger value: certainly not zero at this scale. + STAmount const large{usd, STAmount::kMinValue, 0}; // 1e15 IOU + BEAST_EXPECT(!large.isZeroAtScale(refScale)); + + // When scale equals the value's own exponent, roundToScale + // short-circuits and returns the value unchanged. + BEAST_EXPECT(!subUlp.isZeroAtScale(subUlp.exponent())); + BEAST_EXPECT(!oneUlp.isZeroAtScale(oneUlp.exponent())); + + // Half-ULP boundary. roundToScale forms (value + ref) - ref + // where ref = 10 IOU has mantissa 1e15 (LSB 0, even). + // Number's default rounding is to-nearest-even, so an exact + // half-ULP tie rounds toward the even-LSB neighbour — the + // reference itself — and the round-trip result is zero. + // Just below half-ULP rounds the same way; just above + // clears half-ULP and bumps the mantissa to 1e15 + 1. + STAmount const justBelowHalf{usd, STAmount::kMinValue * 4, -30}; + BEAST_EXPECT(justBelowHalf.isZeroAtScale(refScale)); + + STAmount const halfUlp{usd, STAmount::kMinValue * 5, -30}; + BEAST_EXPECT(halfUlp.isZeroAtScale(refScale)); + + STAmount const justAboveHalf{usd, STAmount::kMinValue * 6, -30}; + BEAST_EXPECT(!justAboveHalf.isZeroAtScale(refScale)); + + // Large magnitude gap: dust value far below an enormous scale. + // 1e-80 with scale +15 — the value vanishes utterly. + STAmount const dust{usd, STAmount::kMinValue, -95}; + BEAST_EXPECT(dust.isZeroAtScale(15)); + + // Negative values mirror positive behaviour. + STAmount const negSubUlp{usd, STAmount::kMinValue, -31, true}; + BEAST_EXPECT(negSubUlp.isZeroAtScale(refScale)); + + STAmount const negOneUlp{usd, STAmount::kMinValue, -29, true}; + BEAST_EXPECT(!negOneUlp.isZeroAtScale(refScale)); + } + + // XRP is integral — roundToScale short-circuits, value is preserved. + { + STAmount const xrp{XRPAmount{1}}; + BEAST_EXPECT(!xrp.isZeroAtScale(-14)); + BEAST_EXPECT(!xrp.isZeroAtScale(0)); + + STAmount const xrpZero{XRPAmount{0}}; + BEAST_EXPECT(xrpZero.isZeroAtScale(-14)); + } + + // MPT is integral — same short-circuit behaviour as XRP. + { + MPTIssue const mpt{makeMptID(1, AccountID(0x4985601))}; + STAmount const mptAmt{mpt, 1}; + BEAST_EXPECT(!mptAmt.isZeroAtScale(0)); + BEAST_EXPECT(!mptAmt.isZeroAtScale(-14)); + + STAmount const mptZero{mpt, 0}; + BEAST_EXPECT(mptZero.isZeroAtScale(0)); + } + } + + //-------------------------------------------------------------------------- + void run() override { @@ -1223,6 +1317,7 @@ public: testCanSubtractXRP(); testCanSubtractIOU(); testCanSubtractMPT(); + testIsZeroAtScale(); } }; From e24de65f42afc48e8f6743a159dcf24d5db2be0a Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Thu, 21 May 2026 18:13:41 +0200 Subject: [PATCH 12/87] chore: Revert graceful peer disconnection and follow-up fix (#7296) --- include/xrpl/server/detail/Door.h | 8 +- src/xrpld/overlay/detail/ConnectAttempt.cpp | 422 ++++++-------------- src/xrpld/overlay/detail/ConnectAttempt.h | 204 +--------- src/xrpld/overlay/detail/PeerImp.cpp | 302 +++++--------- src/xrpld/overlay/detail/PeerImp.h | 205 +--------- 5 files changed, 246 insertions(+), 895 deletions(-) diff --git a/include/xrpl/server/detail/Door.h b/include/xrpl/server/detail/Door.h index e273ca791f..b72bc9bdea 100644 --- a/include/xrpl/server/detail/Door.h +++ b/include/xrpl/server/detail/Door.h @@ -90,11 +90,11 @@ private: acceptor_type acceptor_; boost::asio::strand strand_; bool ssl_{ - port_.protocol.count("https") > 0 || port_.protocol.count("wss") > 0 || - port_.protocol.count("wss2") > 0 || port_.protocol.count("peer") > 0}; + port_.protocol.contains("https") || port_.protocol.contains("wss") || + port_.protocol.contains("wss2") || port_.protocol.contains("peer")}; bool plain_{ - port_.protocol.count("http") > 0 || port_.protocol.count("ws") > 0 || - (port_.protocol.count("ws2") != 0u)}; + port_.protocol.contains("http") || port_.protocol.contains("ws") || + (port_.protocol.contains("ws2"))}; static constexpr std::chrono::milliseconds kInitialAcceptDelay{50}; static constexpr std::chrono::milliseconds kMaxAcceptDelay{2000}; std::chrono::milliseconds acceptDelay_{kInitialAcceptDelay}; diff --git a/src/xrpld/overlay/detail/ConnectAttempt.cpp b/src/xrpld/overlay/detail/ConnectAttempt.cpp index 1167e19ca3..295f4f5497 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.cpp +++ b/src/xrpld/overlay/detail/ConnectAttempt.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -28,19 +29,17 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include #include -#include +#include #include #include @@ -52,7 +51,7 @@ ConnectAttempt::ConnectAttempt( endpoint_type remoteEndpoint, Resource::Consumer usage, shared_context const& context, - std::uint32_t id, + Peer::id_t id, std::shared_ptr const& slot, beast::Journal journal, OverlayImpl& overlay) @@ -65,7 +64,6 @@ ConnectAttempt::ConnectAttempt( , usage_(usage) , strand_(boost::asio::make_strand(ioContext)) , timer_(ioContext) - , stepTimer_(ioContext) , streamPtr_( std::make_unique( socket_type(std::forward(ioContext)), @@ -78,10 +76,9 @@ ConnectAttempt::ConnectAttempt( ConnectAttempt::~ConnectAttempt() { - // slot_ will be null if we successfully connected - // and transferred ownership to a PeerImp if (slot_ != nullptr) overlay_.peerFinder().onClosed(slot_); + JLOG(journal_.trace()) << "~ConnectAttempt"; } void @@ -92,30 +89,17 @@ ConnectAttempt::stop() boost::asio::post(strand_, std::bind(&ConnectAttempt::stop, shared_from_this())); return; } - - if (!socket_.is_open()) - return; - - JLOG(journal_.debug()) << "stop: Stop"; - - shutdown(); + if (socket_.is_open()) + { + JLOG(journal_.debug()) << "Stop"; + } + close(); } void ConnectAttempt::run() { - if (!strand_.running_in_this_thread()) - { - boost::asio::post(strand_, std::bind(&ConnectAttempt::run, shared_from_this())); - return; - } - - JLOG(journal_.debug()) << "run: connecting to " << remoteEndpoint_; - - ioPending_ = true; - - // Allow up to connectTimeout_ seconds to establish remote peer connection - setTimer(ConnectionStep::TcpConnect); + setTimer(); stream_.next_layer().async_connect( remoteEndpoint_, @@ -126,73 +110,6 @@ ConnectAttempt::run() //------------------------------------------------------------------------------ -void -ConnectAttempt::shutdown() -{ - XRPL_ASSERT( - strand_.running_in_this_thread(), "xrpl::ConnectAttempt::shutdown: strand in this thread"); - - if (!socket_.is_open()) - return; - - shutdown_ = true; - boost::beast::get_lowest_layer(stream_).cancel(); - - tryAsyncShutdown(); -} - -void -ConnectAttempt::tryAsyncShutdown() -{ - XRPL_ASSERT( - strand_.running_in_this_thread(), - "xrpl::ConnectAttempt::tryAsyncShutdown : strand in this thread"); - - if (!shutdown_ || currentStep_ == ConnectionStep::ShutdownStarted) - return; - - if (ioPending_) - return; - - // gracefully shutdown the SSL socket, performing a shutdown handshake - if (currentStep_ != ConnectionStep::TcpConnect && currentStep_ != ConnectionStep::TlsHandshake) - { - setTimer(ConnectionStep::ShutdownStarted); - stream_.async_shutdown(bind_executor( - strand_, - std::bind(&ConnectAttempt::onShutdown, shared_from_this(), std::placeholders::_1))); - return; - } - - close(); -} - -void -ConnectAttempt::onShutdown(error_code ec) -{ - cancelTimer(); - - if (ec) - { - // - eof: the stream was cleanly closed - // - operation_aborted: an expired timer (slow shutdown) - // - stream_truncated: the tcp connection closed (no handshake) it could - // occur if a peer does not perform a graceful disconnect - // - broken_pipe: the peer is gone - // - application data after close notify: benign SSL shutdown condition - bool const shouldLog = - (ec != boost::asio::error::eof && ec != boost::asio::error::operation_aborted && - ec.message().find("application data after close notify") == std::string::npos); - - if (shouldLog) - { - JLOG(journal_.debug()) << "onShutdown: " << ec.message(); - } - } - - close(); -} - void ConnectAttempt::close() { @@ -201,93 +118,50 @@ ConnectAttempt::close() if (!socket_.is_open()) return; - cancelTimer(); + try + { + timer_.cancel(); + socket_.close(); + } + catch (boost::system::system_error const&) // NOLINT(bugprone-empty-catch) + { + // ignored + } - error_code ec; - socket_.close(ec); // NOLINT(bugprone-unused-return-value) + JLOG(journal_.debug()) << "Closed"; } void ConnectAttempt::fail(std::string const& reason) { JLOG(journal_.debug()) << reason; - shutdown(); + close(); } void ConnectAttempt::fail(std::string const& name, error_code ec) { JLOG(journal_.debug()) << name << ": " << ec.message(); - shutdown(); + close(); } void -ConnectAttempt::setTimer(ConnectionStep step) +ConnectAttempt::setTimer() { - currentStep_ = step; - - // Set global timer (only if not already set) - if (timer_.expiry() == std::chrono::steady_clock::time_point{}) - { - try - { - timer_.expires_after(kConnectTimeout); - timer_.async_wait( - boost::asio::bind_executor( - strand_, - std::bind( - &ConnectAttempt::onTimer, shared_from_this(), std::placeholders::_1))); - } - catch (std::exception const& ex) - { - JLOG(journal_.error()) << "setTimer (global): " << ex.what(); - close(); - return; - } - } - - // Set step-specific timer try { - std::chrono::seconds stepTimeout; - switch (step) - { - case ConnectionStep::TcpConnect: - stepTimeout = StepTimeouts::kTcpConnect; - break; - case ConnectionStep::TlsHandshake: - stepTimeout = StepTimeouts::kTlsHandshake; - break; - case ConnectionStep::HttpWrite: - stepTimeout = StepTimeouts::kHttpWrite; - break; - case ConnectionStep::HttpRead: - stepTimeout = StepTimeouts::kHttpRead; - break; - case ConnectionStep::ShutdownStarted: - stepTimeout = StepTimeouts::kTlsShutdown; - break; - case ConnectionStep::Complete: - case ConnectionStep::Init: - return; // No timer needed for init or complete step - } - - // call to expires_after cancels previous timer - stepTimer_.expires_after(stepTimeout); - stepTimer_.async_wait( - boost::asio::bind_executor( - strand_, - std::bind(&ConnectAttempt::onTimer, shared_from_this(), std::placeholders::_1))); - - JLOG(journal_.trace()) << "setTimer: " << stepToString(step) - << " timeout=" << stepTimeout.count() << "s"; + timer_.expires_after(std::chrono::seconds(15)); } - catch (std::exception const& ex) + catch (boost::system::system_error const& e) { - JLOG(journal_.error()) << "setTimer (step " << stepToString(step) << "): " << ex.what(); - close(); + JLOG(journal_.error()) << "setTimer: " << e.code(); return; } + + timer_.async_wait( + boost::asio::bind_executor( + strand_, + std::bind(&ConnectAttempt::onTimer, shared_from_this(), std::placeholders::_1))); } void @@ -296,7 +170,6 @@ ConnectAttempt::cancelTimer() try { timer_.cancel(); - stepTimer_.cancel(); } catch (boost::system::system_error const&) // NOLINT(bugprone-empty-catch) { @@ -321,40 +194,18 @@ ConnectAttempt::onTimer(error_code ec) close(); return; } - - // Determine which timer expired by checking their expiry times - auto const now = std::chrono::steady_clock::now(); - bool const globalExpired = (timer_.expiry() <= now); - bool const stepExpired = (stepTimer_.expiry() <= now); - - if (globalExpired) - { - JLOG(journal_.debug()) << "onTimer: Global timeout; step: " << stepToString(currentStep_); - } - else if (stepExpired) - { - JLOG(journal_.debug()) << "onTimer: Step timeout; step: " << stepToString(currentStep_); - } - else - { - JLOG(journal_.warn()) << "onTimer: Unexpected timer callback"; - } - - close(); + fail("Timeout"); } void ConnectAttempt::onConnect(error_code ec) { - ioPending_ = false; + cancelTimer(); if (ec) { if (ec == boost::asio::error::operation_aborted) - { - tryAsyncShutdown(); return; - } fail("onConnect", ec); return; @@ -371,15 +222,7 @@ ConnectAttempt::onConnect(error_code ec) return; } - if (shutdown_) - { - tryAsyncShutdown(); - return; - } - - ioPending_ = true; - - setTimer(ConnectionStep::TlsHandshake); + setTimer(); stream_.set_verify_mode(boost::asio::ssl::verify_none); stream_.async_handshake( @@ -392,15 +235,14 @@ ConnectAttempt::onConnect(error_code ec) void ConnectAttempt::onHandshake(error_code ec) { - ioPending_ = false; + cancelTimer(); + if (!socket_.is_open()) + return; if (ec) { if (ec == boost::asio::error::operation_aborted) - { - tryAsyncShutdown(); return; - } fail("onHandshake", ec); return; @@ -413,21 +255,18 @@ ConnectAttempt::onHandshake(error_code ec) return; } - setTimer(ConnectionStep::HttpWrite); - - // check if we connected to ourselves if (!overlay_.peerFinder().onConnected( slot_, beast::IPAddressConversion::fromAsio(localEndpoint))) { - fail("Self connection"); + fail("Duplicate connection"); return; } auto const sharedValue = makeSharedValue(*streamPtr_, journal_); if (!sharedValue) { - shutdown(); - return; // makeSharedValue logs + close(); // makeSharedValue logs + return; } req_ = makeRequest( @@ -445,14 +284,7 @@ ConnectAttempt::onHandshake(error_code ec) remoteEndpoint_.address(), app_); - if (shutdown_) - { - tryAsyncShutdown(); - return; - } - - ioPending_ = true; - + setTimer(); boost::beast::http::async_write( stream_, req_, @@ -464,30 +296,20 @@ ConnectAttempt::onHandshake(error_code ec) void ConnectAttempt::onWrite(error_code ec) { - ioPending_ = false; + cancelTimer(); + + if (!socket_.is_open()) + return; if (ec) { if (ec == boost::asio::error::operation_aborted) - { - tryAsyncShutdown(); return; - } fail("onWrite", ec); return; } - if (shutdown_) - { - tryAsyncShutdown(); - return; - } - - ioPending_ = true; - - setTimer(ConnectionStep::HttpRead); - boost::beast::http::async_read( stream_, readBuf_, @@ -501,21 +323,24 @@ void ConnectAttempt::onRead(error_code ec) { cancelTimer(); - ioPending_ = false; - currentStep_ = ConnectionStep::Complete; + + if (!socket_.is_open()) + return; if (ec) { + if (ec == boost::asio::error::operation_aborted) + return; + if (ec == boost::asio::error::eof) { JLOG(journal_.debug()) << "EOF"; - shutdown(); - return; - } - - if (ec == boost::asio::error::operation_aborted) - { - tryAsyncShutdown(); + setTimer(); + stream_.async_shutdown( + boost::asio::bind_executor( + strand_, + std::bind( + &ConnectAttempt::onShutdown, shared_from_this(), std::placeholders::_1))); return; } @@ -523,13 +348,25 @@ ConnectAttempt::onRead(error_code ec) return; } - if (shutdown_) + processResponse(); +} + +void +ConnectAttempt::onShutdown(error_code ec) +{ + cancelTimer(); + if (!ec) { - tryAsyncShutdown(); + close(); return; } - processResponse(); + if (ec != boost::asio::error::eof) + { + fail("onShutdown", ec); + return; + } + close(); } //-------------------------------------------------------------------------- @@ -537,71 +374,47 @@ ConnectAttempt::onRead(error_code ec) void ConnectAttempt::processResponse() { - if (!OverlayImpl::isPeerUpgrade(response_)) + if (response_.result() == boost::beast::http::status::service_unavailable) { - // A peer may respond with service_unavailable and a list of alternative - // peers to connect to, a differing status code is unexpected - if (response_.result() != boost::beast::http::status::service_unavailable) - { - JLOG(journal_.warn()) << "Unable to upgrade to peer protocol: " << response_.result() - << " (" << response_.reason() << ")"; - shutdown(); - return; - } - - // Parse response body to determine if this is a redirect or other - // service unavailable - std::string responseBody; - responseBody.reserve(boost::asio::buffer_size(response_.body().data())); + json::Value json; + json::Reader r; + std::string s; + s.reserve(boost::asio::buffer_size(response_.body().data())); for (auto const buffer : response_.body().data()) { - responseBody.append( - static_cast(buffer.data()), boost::asio::buffer_size(buffer)); + s.append(static_cast(buffer.data()), boost::asio::buffer_size(buffer)); } - - json::Value json; - json::Reader reader; - auto const isValidJson = reader.parse(responseBody, json); - - // Check if this is a redirect response (contains peer-ips field) - auto const isRedirect = isValidJson && json.isObject() && json.isMember("peer-ips"); - - if (!isRedirect) + auto const success = r.parse(s, json); + if (success) { - JLOG(journal_.warn()) << "processResponse: " << remoteEndpoint_ - << " failed to upgrade to peer protocol: " << response_.result() - << " (" << response_.reason() << ")"; - - shutdown(); - return; + if (json.isObject() && json.isMember("peer-ips")) + { + json::Value const& ips = json["peer-ips"]; + if (ips.isArray()) + { + std::vector eps; + eps.reserve(ips.size()); + for (auto const& v : ips) + { + if (v.isString()) + { + error_code ec; + auto const ep = parseEndpoint(v.asString(), ec); + if (!ec) + eps.push_back(ep); + } + } + overlay_.peerFinder().onRedirects(remoteEndpoint_, eps); + } + } } + } - json::Value const& peerIps = json["peer-ips"]; - if (!peerIps.isArray()) - { - fail("processResponse: invalid peer-ips format"); - return; - } - - // Extract and validate peer endpoints - std::vector redirectEndpoints; - redirectEndpoints.reserve(peerIps.size()); - - for (auto const& ipValue : peerIps) - { - if (!ipValue.isString()) - continue; - - error_code ec; - auto const endpoint = parseEndpoint(ipValue.asString(), ec); - if (!ec) - redirectEndpoints.push_back(endpoint); - } - - // Notify PeerFinder about the redirect redirectEndpoints may be empty - overlay_.peerFinder().onRedirects(remoteEndpoint_, redirectEndpoints); - - fail("processResponse: failed to connect to peer: redirected"); + if (!OverlayImpl::isPeerUpgrade(response_)) + { + JLOG(journal_.info()) << "Unable to upgrade to peer protocol: " << response_.result() + << " (" << response_.reason() << ")"; + close(); return; } @@ -625,8 +438,8 @@ ConnectAttempt::processResponse() auto const sharedValue = makeSharedValue(*streamPtr_, journal_); if (!sharedValue) { - shutdown(); - return; // makeSharedValue logs + close(); // makeSharedValue logs + return; } try @@ -641,30 +454,21 @@ ConnectAttempt::processResponse() usage_.setPublicKey(publicKey); - JLOG(journal_.debug()) << "Protocol: " << to_string(*negotiatedProtocol); JLOG(journal_.info()) << "Public Key: " << toBase58(TokenType::NodePublic, publicKey); + JLOG(journal_.debug()) << "Protocol: " << to_string(*negotiatedProtocol); + auto const member = app_.getCluster().member(publicKey); if (member) { JLOG(journal_.info()) << "Cluster name: " << *member; } - auto const result = overlay_.peerFinder().activate(slot_, publicKey, member.has_value()); + auto const result = + overlay_.peerFinder().activate(slot_, publicKey, static_cast(member)); if (result != PeerFinder::Result::Success) { - std::stringstream ss; - ss << "Outbound Connect Attempt " << remoteEndpoint_ << " " << to_string(result); - fail(ss.str()); - return; - } - - if (!socket_.is_open()) - return; - - if (shutdown_) - { - tryAsyncShutdown(); + fail("Outbound " + std::string(to_string(result))); return; } diff --git a/src/xrpld/overlay/detail/ConnectAttempt.h b/src/xrpld/overlay/detail/ConnectAttempt.h index 949e6accbf..aba224d5c7 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.h +++ b/src/xrpld/overlay/detail/ConnectAttempt.h @@ -1,42 +1,13 @@ #pragma once +#include #include -#include +#include namespace xrpl { -/** - * @class ConnectAttempt - * @brief Manages outbound peer connection attempts with comprehensive timeout - * handling - * - * The ConnectAttempt class handles the complete lifecycle of establishing an - * outbound connection to a peer in the XRPL network. It implements a - * sophisticated dual-timer system that provides both global timeout protection - * and per-step timeout diagnostics. - * - * The connection establishment follows these steps: - * 1. **TCP Connect**: Establish basic network connection - * 2. **TLS Handshake**: Negotiate SSL/TLS encryption - * 3. **HTTP Write**: Send peer handshake request - * 4. **HTTP Read**: Receive and validate peer response - * 5. **Complete**: Connection successfully established - * - * Uses a hybrid timeout approach: - * - **Global Timer**: Hard limit (20s) for entire connection process - * - **Step Timers**: Individual timeouts for each connection phase - * - * - All errors result in connection termination - * - * All operations are serialized using boost::asio::strand to ensure thread - * safety. The class is designed to be used exclusively within the ASIO event - * loop. - * - * @note This class should not be used directly. It is managed by OverlayImpl - * as part of the peer discovery and connection management system. - * - */ +/** Manages an outbound connection attempt. */ class ConnectAttempt : public OverlayImpl::Child, public std::enable_shared_from_this { @@ -45,95 +16,29 @@ private: using endpoint_type = boost::asio::ip::tcp::endpoint; using request_type = boost::beast::http::request; using response_type = boost::beast::http::response; + using socket_type = boost::asio::ip::tcp::socket; using middle_type = boost::beast::tcp_stream; using stream_type = boost::beast::ssl_stream; using shared_context = std::shared_ptr; - /** - * @enum ConnectionStep - * @brief Represents the current phase of the connection establishment - * process - * - * Used for tracking progress and providing detailed timeout diagnostics. - * Each step has its own timeout value defined in StepTimeouts. - */ - enum class ConnectionStep { - Init, // Initial state, nothing started - TcpConnect, // Establishing TCP connection to remote peer - TlsHandshake, // Performing SSL/TLS handshake - HttpWrite, // Sending HTTP upgrade request - HttpRead, // Reading HTTP upgrade response - Complete, // Connection successfully established - ShutdownStarted // Connection shutdown has started - }; - - // A timeout for connection process, greater than all step timeouts - static constexpr std::chrono::seconds kConnectTimeout{25}; - - /** - * @struct StepTimeouts - * @brief Defines timeout values for each connection step - * - * These timeouts are designed to detect slow individual phases while - * allowing the global timeout to enforce the overall time limit. - */ - struct StepTimeouts - { - // TCP connection timeout - static constexpr std::chrono::seconds kTcpConnect{8}; - // SSL handshake timeout - static constexpr std::chrono::seconds kTlsHandshake{8}; - // HTTP write timeout - static constexpr std::chrono::seconds kHttpWrite{3}; - // HTTP read timeout - static constexpr std::chrono::seconds kHttpRead{3}; - // SSL shutdown timeout - static constexpr std::chrono::seconds kTlsShutdown{2}; - }; - - // Core application and networking components Application& app_; - Peer::id_t const id_; + std::uint32_t const id_; beast::WrappedSink sink_; beast::Journal const journal_; endpoint_type remoteEndpoint_; Resource::Consumer usage_; - boost::asio::strand strand_; boost::asio::basic_waitable_timer timer_; - boost::asio::basic_waitable_timer stepTimer_; - - std::unique_ptr streamPtr_; // SSL stream (owned) + std::unique_ptr streamPtr_; socket_type& socket_; stream_type& stream_; boost::beast::multi_buffer readBuf_; - response_type response_; std::shared_ptr slot_; request_type req_; - bool shutdown_ = false; // Shutdown has been initiated - bool ioPending_ = false; // Async I/O operation in progress - ConnectionStep currentStep_ = ConnectionStep::Init; - public: - /** - * @brief Construct a new ConnectAttempt object - * - * @param app Application context providing configuration and services - * @param ioContext ASIO I/O context for async operations - * @param remoteEndpoint Target peer endpoint to connect to - * @param usage Resource usage tracker for rate limiting - * @param context Shared SSL context for encryption - * @param id Unique peer identifier for this connection attempt - * @param slot PeerFinder slot representing this connection - * @param journal Logging interface for diagnostics - * @param overlay Parent overlay manager - * - * @note The constructor only initializes the object. Call run() to begin - * the actual connection attempt. - */ ConnectAttempt( Application& app, boost::asio::io_context& ioContext, @@ -147,111 +52,38 @@ public: ~ConnectAttempt() override; - /** - * @brief Stop the connection attempt - * - * This method is thread-safe and can be called from any thread. - */ void stop() override; - /** - * @brief Begin the connection attempt - * - * This method is thread-safe and posts to the strand if needed. - */ void run(); private: - /** - * @brief Set timers for the specified connection step - * - * @param step The connection step to set timers for - * - * Sets both the step-specific timer and the global timer (if not already - * set). - */ void - setTimer(ConnectionStep step); - - /** - * @brief Cancel both global and step timers - * - * Used during cleanup and when connection completes successfully. - * Exceptions from timer cancellation are safely ignored. - */ + close(); + void + fail(std::string const& reason); + void + fail(std::string const& name, error_code ec); + void + setTimer(); void cancelTimer(); - - /** - * @brief Handle timer expiration events - * - * @param ec Error code from timer operation - * - * Determines which timer expired (global vs step) and logs appropriate - * diagnostic information before terminating the connection. - */ void onTimer(error_code ec); - - // Connection phase handlers void - onConnect(error_code ec); // TCP connection completion handler + onConnect(error_code ec); void - onHandshake(error_code ec); // TLS handshake completion handler + onHandshake(error_code ec); void - onWrite(error_code ec); // HTTP write completion handler + onWrite(error_code ec); void - onRead(error_code ec); // HTTP read completion handler - - // Error and cleanup handlers + onRead(error_code ec); void - fail(std::string const& reason); // Fail with custom reason - void - fail(std::string const& name, error_code ec); // Fail with system error - void - shutdown(); // Initiate graceful shutdown - void - tryAsyncShutdown(); // Attempt async SSL shutdown - void - onShutdown(error_code ec); // SSL shutdown completion handler - void - close(); // Force close socket - - /** - * @brief Process the HTTP upgrade response from peer - * - * Validates the peer's response, extracts protocol information, - * verifies handshake, and either creates a PeerImp or handles - * redirect responses. - */ + onShutdown(error_code ec); void processResponse(); - static std::string - stepToString(ConnectionStep step) - { - switch (step) - { - case ConnectionStep::Init: - return "Init"; - case ConnectionStep::TcpConnect: - return "TcpConnect"; - case ConnectionStep::TlsHandshake: - return "TlsHandshake"; - case ConnectionStep::HttpWrite: - return "HttpWrite"; - case ConnectionStep::HttpRead: - return "HttpRead"; - case ConnectionStep::Complete: - return "Complete"; - case ConnectionStep::ShutdownStarted: - return "ShutdownStarted"; - } - return "Unknown"; - }; - template static boost::asio::ip::tcp::endpoint parseEndpoint(std::string const& s, boost::system::error_code& ec) diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 23e1785f6b..325f8ba038 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -74,7 +74,7 @@ #include #include #include -#include +#include #include @@ -110,9 +110,6 @@ constexpr std::chrono::milliseconds kPeerHighLatency{300}; /** How often we PING the peer to check for latency and sendq probe */ constexpr std::chrono::seconds kPeerTimerInterval{60}; -/** The timeout for a shutdown timer */ -constexpr std::chrono::seconds kShutdownTimerInterval{5}; - } // namespace // TODO: Remove this exclusion once unit tests are added after the hotfix @@ -270,13 +267,7 @@ PeerImp::stop() if (!socket_.is_open()) return; - // The rationale for using different severity levels is that - // outbound connections are under our control and may be logged - // at a higher level, but inbound connections are more numerous and - // uncontrolled so to prevent log flooding the severity is reduced. - JLOG(journal_.debug()) << "stop: Stop"; - - shutdown(); + close(); } //------------------------------------------------------------------------------ @@ -289,17 +280,13 @@ PeerImp::send(std::shared_ptr const& m) post(strand_, std::bind(&PeerImp::send, shared_from_this(), m)); return; } - + if (gracefulClose_) + return; + if (detaching_) + return; if (!socket_.is_open()) return; - // we are in progress of closing the connection - if (shutdown_) - { - tryAsyncShutdown(); - return; - } - auto validator = m->getValidatorKey(); if (validator && !squelch_.expireSquelch(*validator)) { @@ -338,7 +325,6 @@ PeerImp::send(std::shared_ptr const& m) if (sendqSize != 0) return; - writePending_ = true; boost::asio::async_write( stream_, boost::asio::buffer(sendQueue_.front()->getBuffer(compressionEnabled_)), @@ -621,16 +607,20 @@ PeerImp::hasRange(std::uint32_t uMin, std::uint32_t uMax) //------------------------------------------------------------------------------ void -PeerImp::fail(std::string const& name, error_code ec) +PeerImp::close() { - XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::fail : strand in this thread"); - + XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::close : strand in this thread"); if (!socket_.is_open()) return; - JLOG(journal_.warn()) << name << ": " << ec.message(); + detaching_ = true; // DEPRECATED - shutdown(); + cancelTimer(); + error_code ec; + socket_.close(ec); // NOLINT(bugprone-unused-return-value) + + overlay_.incPeerDisconnect(); + JLOG((inbound_ ? journal_.debug() : journal_.info())) << "close: Closed"; } void @@ -644,123 +634,71 @@ PeerImp::fail(std::string const& reason) (void (Peer::*)(std::string const&))&PeerImp::fail, shared_from_this(), reason)); return; } - - if (!socket_.is_open()) - return; - - // Call to name() locks, log only if the message will be outputted - if (journal_.active(beast::Severity::Warning)) + if (journal_.active(beast::Severity::Warning) && socket_.is_open()) { std::string const n = name(); JLOG(journal_.warn()) << n << " failed: " << reason; } - - shutdown(); + close(); } void -PeerImp::tryAsyncShutdown() +PeerImp::fail(std::string const& name, error_code ec) { - XRPL_ASSERT( - strand_.running_in_this_thread(), - "xrpl::PeerImp::tryAsyncShutdown : strand in this thread"); - - if (!shutdown_ || shutdownStarted_) + XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::fail : strand in this thread"); + if (!socket_.is_open()) return; - if (readPending_ || writePending_) - return; - - shutdownStarted_ = true; - - setTimer(kShutdownTimerInterval); - - // gracefully shutdown the SSL socket, performing a shutdown handshake - stream_.async_shutdown(bind_executor( - strand_, std::bind(&PeerImp::onShutdown, shared_from_this(), std::placeholders::_1))); -} - -void -PeerImp::shutdown() -{ - XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::shutdown: strand in this thread"); - - if (!socket_.is_open() || shutdown_) - return; - - shutdown_ = true; - - boost::beast::get_lowest_layer(stream_).cancel(); - - tryAsyncShutdown(); -} - -void -PeerImp::onShutdown(error_code ec) -{ - cancelTimer(); - if (ec) - { - // - eof: the stream was cleanly closed - // - operation_aborted: an expired timer (slow shutdown) - // - stream_truncated: the tcp connection closed (no handshake) it could - // occur if a peer does not perform a graceful disconnect - // - broken_pipe: the peer is gone - bool const shouldLog = - (ec != boost::asio::error::eof && ec != boost::asio::error::operation_aborted && - ec.message().find("application data after close notify") == std::string::npos); - - if (shouldLog) - { - JLOG(journal_.debug()) << "onShutdown: " << ec.message(); - } - } + JLOG(journal_.warn()) << name << ": " << ec.message(); close(); } void -PeerImp::close() +PeerImp::gracefulClose() { - XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::close : strand in this thread"); - - if (!socket_.is_open()) + XRPL_ASSERT( + strand_.running_in_this_thread(), "xrpl::PeerImp::gracefulClose : strand in this thread"); + XRPL_ASSERT(socket_.is_open(), "xrpl::PeerImp::gracefulClose : socket is open"); + XRPL_ASSERT(!gracefulClose_, "xrpl::PeerImp::gracefulClose : socket is not closing"); + gracefulClose_ = true; + if (!sendQueue_.empty()) return; - - cancelTimer(); - - error_code ec; - socket_.close(ec); // NOLINT(bugprone-unused-return-value) - - overlay_.incPeerDisconnect(); - - // The rationale for using different severity levels is that - // outbound connections are under our control and may be logged - // at a higher level, but inbound connections are more numerous and - // uncontrolled so to prevent log flooding the severity is reduced. - JLOG((inbound_ ? journal_.debug() : journal_.info())) << "close: Closed"; + setTimer(); + stream_.async_shutdown(bind_executor( + strand_, std::bind(&PeerImp::onShutdown, shared_from_this(), std::placeholders::_1))); } -//------------------------------------------------------------------------------ - void -PeerImp::setTimer(std::chrono::seconds interval) +PeerImp::setTimer() { try { - timer_.expires_after(interval); + timer_.expires_after(kPeerTimerInterval); } - catch (std::exception const& ex) + catch (boost::system::system_error const& e) { - JLOG(journal_.error()) << "setTimer: " << ex.what(); - shutdown(); + JLOG(journal_.error()) << "setTimer: " << e.code(); return; } - timer_.async_wait(bind_executor( strand_, std::bind(&PeerImp::onTimer, shared_from_this(), std::placeholders::_1))); } +// convenience for ignoring the error code +void +PeerImp::cancelTimer() noexcept +{ + try + { + timer_.cancel(); + } + catch (boost::system::system_error const&) // NOLINT(bugprone-empty-catch) + { + // ignored + } +} + //------------------------------------------------------------------------------ std::string @@ -774,14 +712,11 @@ PeerImp::makePrefix(std::string const& fingerprint) void PeerImp::onTimer(error_code const& ec) { - XRPL_ASSERT(strand_.running_in_this_thread(), "xrpl::PeerImp::onTimer : strand in this thread"); - if (!socket_.is_open()) return; if (ec) { - // do not initiate shutdown, timers are frequently cancelled if (ec == boost::asio::error::operation_aborted) return; @@ -791,15 +726,6 @@ PeerImp::onTimer(error_code const& ec) return; } - // the timer expired before the shutdown completed - // force close the connection - if (shutdown_) - { - JLOG(journal_.debug()) << "onTimer: shutdown timer expired"; - close(); - return; - } - if (largeSendq_++ >= Tuning::kSendqIntervals) { fail("Large send queue"); @@ -840,20 +766,32 @@ PeerImp::onTimer(error_code const& ec) send(std::make_shared(message, protocol::mtPING)); - setTimer(kPeerTimerInterval); + setTimer(); } void -PeerImp::cancelTimer() noexcept +PeerImp::onShutdown(error_code ec) { - try + cancelTimer(); + + if (ec) { - timer_.cancel(); - } - catch (std::exception const& ex) - { - JLOG(journal_.error()) << "cancelTimer: " << ex.what(); + // - eof: the stream was cleanly closed + // - operation_aborted: an expired timer (slow shutdown) + // - stream_truncated: the tcp connection closed (no handshake) it could + // occur if a peer does not perform a graceful disconnect + // - broken_pipe: the peer is gone + bool const shouldLog = + (ec != boost::asio::error::eof && ec != boost::asio::error::operation_aborted && + ec.message().find("application data after close notify") == std::string::npos); + + if (shouldLog) + { + JLOG(journal_.debug()) << "onShutdown: " << ec.message(); + } } + + close(); } //------------------------------------------------------------------------------ @@ -862,15 +800,6 @@ PeerImp::doAccept() { XRPL_ASSERT(readBuffer_.size() == 0, "xrpl::PeerImp::doAccept : empty read buffer"); - JLOG(journal_.debug()) << "doAccept"; - - // a shutdown was initiated before the handshake, there is nothing to do - if (shutdown_) - { - tryAsyncShutdown(); - return; - } - auto const sharedValue = makeSharedValue(*streamPtr_, journal_); // This shouldn't fail since we already computed @@ -881,7 +810,7 @@ PeerImp::doAccept() return; } - JLOG(journal_.debug()) << "Protocol: " << to_string(protocol_); + JLOG(journal_.info()) << "Protocol: " << to_string(protocol_); if (auto member = app_.getCluster().member(publicKey_)) { @@ -921,16 +850,15 @@ PeerImp::doAccept() error_code ec, std::size_t bytesTransferred) { if (!socket_.is_open()) return; - if (ec == boost::asio::error::operation_aborted) - { - tryAsyncShutdown(); - return; - } if (ec) { + if (ec == boost::asio::error::operation_aborted) + return; + fail("onWriteResponse", ec); return; } + if (writeBuffer->size() == bytesTransferred) { doProtocolStart(); @@ -961,13 +889,6 @@ PeerImp::domain() const void PeerImp::doProtocolStart() { - // a shutdown was initiated before the handshare, there is nothing to do - if (shutdown_) - { - tryAsyncShutdown(); - return; - } - onReadMessage(error_code(), 0); // Send all the validator lists that have been loaded @@ -999,45 +920,31 @@ PeerImp::doProtocolStart() if (auto m = overlay_.getManifestsMessage()) send(m); - setTimer(kPeerTimerInterval); + setTimer(); } // Called repeatedly with protocol message data void PeerImp::onReadMessage(error_code ec, std::size_t bytesTransferred) { - XRPL_ASSERT( - strand_.running_in_this_thread(), "xrpl::PeerImp::onReadMessage : strand in this thread"); - - readPending_ = false; - if (!socket_.is_open()) return; if (ec) { + if (ec == boost::asio::error::operation_aborted) + return; + if (ec == boost::asio::error::eof) { - JLOG(journal_.debug()) << "EOF"; - shutdown(); - return; - } - - if (ec == boost::asio::error::operation_aborted) - { - tryAsyncShutdown(); + JLOG(journal_.info()) << "EOF"; + gracefulClose(); return; } fail("onReadMessage", ec); return; } - // we started shutdown, no reason to process further data - if (shutdown_) - { - tryAsyncShutdown(); - return; - } if (auto stream = journal_.trace()) { @@ -1062,34 +969,23 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytesTransferred) 350ms, journal_); - if (!socket_.is_open()) - return; - - // the error_code is produced by invokeProtocolMessage - // it could be due to a bad message if (ec) { fail("onReadMessage", ec); return; } + if (!socket_.is_open()) + return; + + if (gracefulClose_) + return; + if (bytesConsumed == 0) break; - readBuffer_.consume(bytesConsumed); } - // check if a shutdown was initiated while processing messages - if (shutdown_) - { - tryAsyncShutdown(); - return; - } - - readPending_ = true; - - XRPL_ASSERT(!shutdownStarted_, "xrpl::PeerImp::onReadMessage : shutdown started"); - // Timeout on writes only stream_.async_read_some( readBuffer_.prepare(std::max(Tuning::kReadBufferBytes, hint)), @@ -1105,26 +1001,17 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytesTransferred) void PeerImp::onWriteMessage(error_code ec, std::size_t bytesTransferred) { - XRPL_ASSERT( - strand_.running_in_this_thread(), "xrpl::PeerImp::onWriteMessage : strand in this thread"); - - writePending_ = false; - if (!socket_.is_open()) return; if (ec) { if (ec == boost::asio::error::operation_aborted) - { - tryAsyncShutdown(); return; - } fail("onWriteMessage", ec); return; } - if (auto stream = journal_.trace()) { stream << "onWriteMessage: " @@ -1135,18 +1022,8 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytesTransferred) XRPL_ASSERT(!sendQueue_.empty(), "xrpl::PeerImp::onWriteMessage : non-empty send buffer"); sendQueue_.pop(); - - if (shutdown_) - { - tryAsyncShutdown(); - return; - } - if (!sendQueue_.empty()) { - writePending_ = true; - XRPL_ASSERT(!shutdownStarted_, "xrpl::PeerImp::onWriteMessage : shutdown started"); - // Timeout on writes only boost::asio::async_write( stream_, @@ -1160,6 +1037,13 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytesTransferred) std::placeholders::_2))); return; } + + if (gracefulClose_) + { + stream_.async_shutdown(bind_executor( + strand_, std::bind(&PeerImp::onShutdown, shared_from_this(), std::placeholders::_1))); + return; + } } //------------------------------------------------------------------------------ diff --git a/src/xrpld/overlay/detail/PeerImp.h b/src/xrpld/overlay/detail/PeerImp.h index dca70d88bd..f5d87371be 100644 --- a/src/xrpld/overlay/detail/PeerImp.h +++ b/src/xrpld/overlay/detail/PeerImp.h @@ -30,68 +30,6 @@ namespace xrpl { struct ValidatorBlobInfo; class SHAMap; -/** - * @class PeerImp - * @brief This class manages established peer-to-peer connections, handles - message exchange, monitors connection health, and graceful shutdown. - * - - * The PeerImp shutdown mechanism is a multi-stage process - * designed to ensure graceful connection termination while handling ongoing - * I/O operations safely. The shutdown can be initiated from multiple points - * and follows a deterministic state machine. - * - * The shutdown process can be triggered from several entry points: - * - **External requests**: `stop()` method called by overlay management - * - **Error conditions**: `fail(error_code)` or `fail(string)` on protocol - * violations - * - **Timer expiration**: Various timeout scenarios (ping timeout, large send - * queue) - * - **Connection health**: Peer tracking divergence or unknown state timeouts - * - * The shutdown follows this progression: - * - * Normal Operation → shutdown() → tryAsyncShutdown() → onShutdown() → close() - * ↓ ↓ ↓ ↓ - * Set shutdown_ SSL graceful Timer cancel Socket close - * Cancel timer shutdown start & cleanup & metrics - * 5s safety timer Set shutdownStarted_ update - * - * Two primary flags coordinate the shutdown process: - * - `shutdown_`: Set when shutdown is requested - * - `shutdownStarted_`: Set when SSL shutdown begins - * - * The shutdown mechanism carefully coordinates with ongoing read/write - * operations: - * - * **Read Operations (`onReadMessage`)**: - * - Checks `shutdown_` flag after processing each message batch - * - If shutdown initiated during processing, calls `tryAsyncShutdown()` - * - * **Write Operations (`onWriteMessage`)**: - * - Checks `shutdown_` flag before queuing new writes - * - Calls `tryAsyncShutdown()` when shutdown flag detected - * - * Multiple timers require coordination during shutdown: - * 1. **Peer Timer**: Regular ping/pong timer cancelled immediately in - * `shutdown()` - * 2. **Shutdown Timer**: 5-second safety timer ensures shutdown completion - * 3. **Operation Cancellation**: All pending async operations are cancelled - * - * The shutdown implements fallback mechanisms: - * - **Graceful Path**: SSL shutdown → Socket close → Cleanup - * - **Forced Path**: If SSL shutdown fails or times out, proceeds to socket - * close - * - **Safety Timer**: 5-second timeout prevents hanging shutdowns - * - * All shutdown operations are serialized through the boost::asio::strand to - * ensure thread safety. The strand guarantees that shutdown state changes - * and I/O operation callbacks are executed sequentially. - * - * @note This class requires careful coordination between async operations, - * timer management, and shutdown procedures to ensure no resource leaks - * or hanging connections in high-throughput networking scenarios. - */ class PeerImp : public Peer, public std::enable_shared_from_this, public OverlayImpl::Child { public: @@ -121,8 +59,6 @@ private: socket_type& socket_; stream_type& stream_; boost::asio::strand strand_; - - // Multi-purpose timer for peer activity monitoring and shutdown safety waitable_timer timer_; // Updated at each stage of the connection process to reflect @@ -139,6 +75,7 @@ private: std::atomic tracking_; clock_type::time_point trackingTime_; + bool detaching_ = false; // Node public key of peer. PublicKey const publicKey_; std::string name_; @@ -216,19 +153,7 @@ private: http_response_type response_; boost::beast::http::fields const& headers_; std::queue> sendQueue_; - - // Primary shutdown flag set when shutdown is requested - bool shutdown_ = false; - - // SSL shutdown coordination flag - bool shutdownStarted_ = false; - - // Indicates a read operation is currently pending - bool readPending_ = false; - - // Indicates a write operation is currently pending - bool writePending_ = false; - + bool gracefulClose_ = false; int largeSendq_ = 0; std::unique_ptr loadEvent_; // The highest sequence of each PublisherList that has @@ -476,6 +401,9 @@ public: bool isHighLatency() const override; + void + fail(std::string const& reason); + bool compressionEnabled() const override { @@ -489,129 +417,32 @@ public: } private: - /** - * @brief Handles a failure associated with a specific error code. - * - * This function is called when an operation fails with an error code. It - * logs the warning message and gracefully shutdowns the connection. - * - * The function will do nothing if the connection is already closed or if a - * shutdown is already in progress. - * - * @param name The name of the operation that failed (e.g., "read", - * "write"). - * @param ec The error code associated with the failure. - * @note This function must be called from within the object's strand. - */ - void - fail(std::string const& name, error_code ec); - - /** - * @brief Handles a failure described by a reason string. - * - * This overload is used for logical errors or protocol violations not - * associated with a specific error code. It logs a warning with the - * given reason, then initiates a graceful shutdown. - * - * The function will do nothing if the connection is already closed or if a - * shutdown is already in progress. - * - * @param reason A descriptive string explaining the reason for the failure. - * @note This function must be called from within the object's strand. - */ - void - fail(std::string const& reason); - - /** @brief Initiates the peer disconnection sequence. - * - * This is the primary entry point to start closing a peer connection. It - * marks the peer for shutdown and cancels any outstanding asynchronous - * operations. This cancellation allows the graceful shutdown to proceed - * once the handlers for the cancelled operations have completed. - * - * @note This method must be called on the peer's strand. - */ - void - shutdown(); - - /** @brief Attempts to perform a graceful SSL shutdown if conditions are - * met. - * - * This helper function checks if the peer is in a state where a graceful - * SSL shutdown can be performed (i.e., shutdown has been requested and no - * I/O operations are currently in progress). - * - * @note This method must be called on the peer's strand. - */ - void - tryAsyncShutdown(); - - /** - * @brief Handles the completion of the asynchronous SSL shutdown. - * - * This function is the callback for the `async_shutdown` operation started - * in `shutdown()`. Its first action is to cancel the timer. It - * then inspects the error code to determine the outcome. - * - * Regardless of the result, this function proceeds to call `close()` to - * ensure the underlying socket is fully closed. - * - * @param ec The error code resulting from the `async_shutdown` operation. - */ - void - onShutdown(error_code ec); - - /** - * @brief Forcibly closes the underlying socket connection. - * - * This function provides the final, non-graceful shutdown of the peer - * connection. It ensures any pending timers are cancelled and then - * immediately closes the TCP socket, bypassing the SSL shutdown handshake. - * - * After closing, it notifies the overlay manager of the disconnection. - * - * @note This function must be called from within the object's strand. - */ void close(); - /** - * @brief Sets and starts the peer timer. - * - * This function starts timer, which is used to detect inactivity - * and prevent stalled connections. It sets the timer to expire after the - * predefined `peerTimerInterval`. - * - * @note This function will terminate the connection in case of any errors. - */ void - setTimer(std::chrono::seconds interval); + fail(std::string const& name, error_code ec); - /** - * @brief Handles the expiration of the peer activity timer. - * - * This callback is invoked when the timer set by `setTimer` expires. It - * watches the peer connection, checking for various timeout and health - * conditions. - * - * @param ec The error code associated with the timer's expiration. - * `operation_aborted` is expected if the timer was cancelled. - */ void - onTimer(error_code const& ec); + gracefulClose(); + + void + setTimer(); - /** - * @brief Cancels any pending wait on the peer activity timer. - * - * This function is called to stop the timer. It gracefully manages any - * errors that might occur during the cancellation process. - */ void cancelTimer() noexcept; static std::string makePrefix(std::string const& fingerprint); + // Called when the timer wait completes + void + onTimer(boost::system::error_code const& ec); + + // Called when SSL shutdown completes + void + onShutdown(error_code ec); + void doAccept(); From 79308705c5f90440b81a3a371807a40d357df95e Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 21 May 2026 18:50:59 +0100 Subject: [PATCH 13/87] release: Bump version to 3.2.0-b6 (#7311) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 820936a22d..3d4fff9f04 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -23,7 +23,7 @@ namespace { //------------------------------------------------------------------------------ // clang-format off // NOLINTNEXTLINE(readability-identifier-naming) -char const* const versionString = "3.3.0-b0" +char const* const versionString = "3.2.0-b6" // clang-format on ; From 1a98182e23ec17b508fa6a2de6f4aef0600218c9 Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 21 May 2026 18:52:41 +0100 Subject: [PATCH 14/87] refactor: Remove dead `fetchBatch` code (#7309) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- include/xrpl/nodestore/Backend.h | 4 --- .../xrpl/nodestore/detail/DatabaseNodeImp.h | 3 -- src/libxrpl/nodestore/DatabaseNodeImp.cpp | 34 ------------------- .../nodestore/backend/MemoryFactory.cpp | 23 ------------- src/libxrpl/nodestore/backend/NuDBFactory.cpp | 23 ------------- src/libxrpl/nodestore/backend/NullFactory.cpp | 8 ----- .../nodestore/backend/RocksDBFactory.cpp | 24 ------------- 7 files changed, 119 deletions(-) diff --git a/include/xrpl/nodestore/Backend.h b/include/xrpl/nodestore/Backend.h index 124cd99db5..0061890237 100644 --- a/include/xrpl/nodestore/Backend.h +++ b/include/xrpl/nodestore/Backend.h @@ -83,10 +83,6 @@ public: virtual Status fetch(uint256 const& hash, std::shared_ptr* pObject) = 0; - /** Fetch a batch synchronously. */ - virtual std::pair>, Status> - fetchBatch(std::vector const& hashes) = 0; - /** Store a single object. Depending on the implementation this may happen immediately or deferred using a scheduled task. diff --git a/include/xrpl/nodestore/detail/DatabaseNodeImp.h b/include/xrpl/nodestore/detail/DatabaseNodeImp.h index add66c51a7..dd94b27075 100644 --- a/include/xrpl/nodestore/detail/DatabaseNodeImp.h +++ b/include/xrpl/nodestore/detail/DatabaseNodeImp.h @@ -67,9 +67,6 @@ public: backend_->sync(); } - std::vector> - fetchBatch(std::vector const& hashes); - void asyncFetch( uint256 const& hash, diff --git a/src/libxrpl/nodestore/DatabaseNodeImp.cpp b/src/libxrpl/nodestore/DatabaseNodeImp.cpp index ef84862de6..a51c34079b 100644 --- a/src/libxrpl/nodestore/DatabaseNodeImp.cpp +++ b/src/libxrpl/nodestore/DatabaseNodeImp.cpp @@ -4,21 +4,16 @@ #include #include #include -#include -#include #include #include #include #include -#include -#include #include #include #include #include #include -#include namespace xrpl::NodeStore { @@ -81,33 +76,4 @@ DatabaseNodeImp::fetchNodeObject( return nodeObject; } -std::vector> -DatabaseNodeImp::fetchBatch(std::vector const& hashes) -{ - using namespace std::chrono; - auto const before = steady_clock::now(); - - // Get the node objects that match the hashes from the backend. To protect - // against the backends returning fewer or more results than expected, the - // container is resized to the number of hashes. - auto results = backend_->fetchBatch(hashes).first; - XRPL_ASSERT( - results.size() == hashes.size() || results.empty(), - "number of output objects either matches number of input hashes or is empty"); - results.resize(hashes.size()); - for (size_t i = 0; i < results.size(); ++i) - { - if (!results[i]) - { - JLOG(j_.error()) << "fetchBatch - " - << "record not found in db. hash = " << strHex(hashes[i]); - } - } - - auto fetchDurationUs = - std::chrono::duration_cast(steady_clock::now() - before).count(); - updateFetchMetrics(hashes.size(), 0, fetchDurationUs); - return results; -} - } // namespace xrpl::NodeStore diff --git a/src/libxrpl/nodestore/backend/MemoryFactory.cpp b/src/libxrpl/nodestore/backend/MemoryFactory.cpp index 621ad1357c..5bdf8e65b5 100644 --- a/src/libxrpl/nodestore/backend/MemoryFactory.cpp +++ b/src/libxrpl/nodestore/backend/MemoryFactory.cpp @@ -22,7 +22,6 @@ #include #include #include -#include namespace xrpl::NodeStore { @@ -146,28 +145,6 @@ public: return Status::Ok; } - std::pair>, Status> - fetchBatch(std::vector const& hashes) override - { - std::vector> results; - results.reserve(hashes.size()); - for (auto const& h : hashes) - { - std::shared_ptr nObj; - Status const status = fetch(h, &nObj); - if (status != Status::Ok) - { - results.push_back({}); - } - else - { - results.push_back(nObj); - } - } - - return {results, Status::Ok}; - } - void store(std::shared_ptr const& object) override { diff --git a/src/libxrpl/nodestore/backend/NuDBFactory.cpp b/src/libxrpl/nodestore/backend/NuDBFactory.cpp index d026dc254c..abf09e871d 100644 --- a/src/libxrpl/nodestore/backend/NuDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/NuDBFactory.cpp @@ -42,7 +42,6 @@ #include #include #include -#include namespace xrpl::NodeStore { @@ -232,28 +231,6 @@ public: return status; } - std::pair>, Status> - fetchBatch(std::vector const& hashes) override - { - std::vector> results; - results.reserve(hashes.size()); - for (auto const& h : hashes) - { - std::shared_ptr nObj; - Status const status = fetch(h, &nObj); - if (status != Status::Ok) - { - results.push_back({}); - } - else - { - results.push_back(nObj); - } - } - - return {results, Status::Ok}; - } - void doInsert(std::shared_ptr const& no) { diff --git a/src/libxrpl/nodestore/backend/NullFactory.cpp b/src/libxrpl/nodestore/backend/NullFactory.cpp index ae33b7daa2..36b8139984 100644 --- a/src/libxrpl/nodestore/backend/NullFactory.cpp +++ b/src/libxrpl/nodestore/backend/NullFactory.cpp @@ -12,8 +12,6 @@ #include #include #include -#include -#include namespace xrpl::NodeStore { @@ -52,12 +50,6 @@ public: return Status::NotFound; } - std::pair>, Status> - fetchBatch(std::vector const& hashes) override - { - return {}; - } - void store(std::shared_ptr const& object) override { diff --git a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp index c1baf6aaaa..d2c193888c 100644 --- a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp @@ -29,8 +29,6 @@ #include #include #include -#include -#include #if XRPL_ROCKSDB_AVAILABLE #include @@ -330,28 +328,6 @@ public: return status; } - std::pair>, Status> - fetchBatch(std::vector const& hashes) override - { - std::vector> results; - results.reserve(hashes.size()); - for (auto const& h : hashes) - { - std::shared_ptr nObj; - Status const status = fetch(h, &nObj); - if (status != Status::Ok) - { - results.push_back({}); - } - else - { - results.push_back(nObj); - } - } - - return {results, Status::Ok}; - } - void store(std::shared_ptr const& object) override { From 3547a9335f148fa85c0e23831612fb3f872e9b04 Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Thu, 21 May 2026 14:29:53 -0400 Subject: [PATCH 15/87] fix: Add assorted MPT/DEX fixes (#7040) Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com> Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com> --- include/xrpl/ledger/helpers/MPTokenHelpers.h | 22 +- include/xrpl/ledger/helpers/TokenHelpers.h | 6 + include/xrpl/tx/invariants/InvariantCheck.h | 3 +- include/xrpl/tx/invariants/MPTInvariant.h | 38 ++ src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 116 +---- src/libxrpl/ledger/helpers/TokenHelpers.cpp | 24 + src/libxrpl/protocol/STPathSet.cpp | 10 +- src/libxrpl/tx/invariants/AMMInvariant.cpp | 33 +- src/libxrpl/tx/invariants/InvariantCheck.cpp | 2 +- src/libxrpl/tx/invariants/MPTInvariant.cpp | 150 +++++- src/libxrpl/tx/paths/BookStep.cpp | 35 +- src/libxrpl/tx/paths/MPTEndpointStep.cpp | 12 +- .../tx/transactors/check/CheckCash.cpp | 5 +- .../tx/transactors/check/CheckCreate.cpp | 20 +- src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 34 +- src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 29 +- .../tx/transactors/dex/AMMWithdraw.cpp | 29 +- .../tx/transactors/dex/OfferCreate.cpp | 20 +- .../tx/transactors/token/MPTokenAuthorize.cpp | 5 +- src/test/app/AMMExtendedMPT_test.cpp | 6 +- src/test/app/AMMMPT_test.cpp | 90 ++-- src/test/app/CheckMPT_test.cpp | 8 +- src/test/app/ClawbackMPT_test.cpp | 435 +++++++++++++++++ src/test/app/Invariants_test.cpp | 184 +++++++ src/test/app/MPToken_test.cpp | 458 ++++++++++++------ src/test/app/OfferMPT_test.cpp | 184 ++++++- src/test/app/Vault_test.cpp | 81 +++- 27 files changed, 1618 insertions(+), 421 deletions(-) create mode 100644 src/test/app/ClawbackMPT_test.cpp diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h index 491bd1933e..c709badab8 100644 --- a/include/xrpl/ledger/helpers/MPTokenHelpers.h +++ b/include/xrpl/ledger/helpers/MPTokenHelpers.h @@ -171,6 +171,15 @@ canTransfer( [[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. + */ +[[nodiscard]] TER +canMPTTradeAndTransfer( + ReadView const& v, + Asset const& asset, + AccountID const& from, + AccountID const& to); + //------------------------------------------------------------------------------ // // Empty holding operations (MPT-specific) @@ -277,17 +286,4 @@ issuerFundsToSelfIssue(ReadView const& view, MPTIssue const& issue); void issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amount); -//------------------------------------------------------------------------------ -// -// MPT DEX -// -//------------------------------------------------------------------------------ - -/* Return true if a transaction is allowed for the specified MPT/account. The - * function checks MPTokenIssuance and MPToken objects flags to determine if the - * transaction is allowed. - */ -TER -checkMPTTxAllowed(ReadView const& v, TxType tx, Asset const& asset, AccountID const& accountID); - } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/TokenHelpers.h b/include/xrpl/ledger/helpers/TokenHelpers.h index b47286a5d5..f736e51d28 100644 --- a/include/xrpl/ledger/helpers/TokenHelpers.h +++ b/include/xrpl/ledger/helpers/TokenHelpers.h @@ -63,9 +63,15 @@ enum class AuthType { StrongAuth, WeakAuth, Legacy }; [[nodiscard]] bool isGlobalFrozen(ReadView const& view, Asset const& asset); +[[nodiscard]] TER +checkGlobalFrozen(ReadView const& view, Asset const& asset); + [[nodiscard]] bool isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset); +[[nodiscard]] TER +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 diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index a58ac0df50..5996039bf0 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -401,7 +401,8 @@ using InvariantChecks = std::tuple< ValidLoanBroker, ValidLoan, ValidVault, - ValidMPTPayment>; + ValidMPTPayment, + ValidMPTTransfer>; /** * @brief get a tuple of all invariant checks diff --git a/include/xrpl/tx/invariants/MPTInvariant.h b/include/xrpl/tx/invariants/MPTInvariant.h index f2b0a18131..becb752126 100644 --- a/include/xrpl/tx/invariants/MPTInvariant.h +++ b/include/xrpl/tx/invariants/MPTInvariant.h @@ -71,4 +71,42 @@ public: finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); }; +class ValidMPTTransfer +{ + struct Value + { + std::optional amtBefore; + std::optional amtAfter; + }; + // MPTID: {holder: Value} + hash_map> amount_; + // Deleted MPToken + // MPToken key: true if MPTAuthorized is set + hash_map deletedAuthorized_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); + +private: + /** + * @brief Check whether a holder is authorized to send or receive an MPToken. + * + * Deleted MPToken SLEs are no longer present in the view by the time + * finalize() runs, so their authorization state is captured during + * visitEntry() and stored in deletedAuthorized_. For deleted MPTokens, + * returns true if reqAuth is false or lsfMPTAuthorized was set at deletion. + * For existing MPTokens, returns the result of requireAuth() + */ + [[nodiscard]] bool + isAuthorized( + ReadView const& view, + MPTID const& mptid, + AccountID const& holder, + bool requireAuth) const; +}; + } // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index 542a5b60ed..387116d820 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -383,10 +382,11 @@ requireAuth( // belong to someone who is explicitly authorized e.g. a vault owner. } - if (featureSAVEnabled) + bool const featureMPTV2Enabled = view.rules().enabled(featureMPTokensV2); + if (featureSAVEnabled || featureMPTV2Enabled) { - // Implicitly authorize Vault and LoanBroker pseudo-accounts - if (isPseudoAccount(view, account, {&sfVaultID, &sfLoanBrokerID})) + // Implicitly authorize Vault, LoanBroker, and AMM pseudo-accounts + if (isPseudoAccount(view, account, {&sfVaultID, &sfLoanBrokerID, &sfAMMID})) return tesSUCCESS; } @@ -618,6 +618,22 @@ canTrade(ReadView const& view, Asset const& asset, std::uint8_t depth) }); } +TER +canMPTTradeAndTransfer( + ReadView const& view, + Asset const& asset, + AccountID const& from, + AccountID const& to) +{ + if (!asset.holds()) + return tesSUCCESS; + + if (auto const ter = canTrade(view, asset); !isTesSuccess(ter)) + return ter; + + return canTransfer(view, asset, from, to); +} + TER lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount, beast::Journal j) { @@ -985,96 +1001,4 @@ issuerSelfDebitHookMPT(ApplyView& view, MPTIssue const& issue, std::uint64_t amo view.issuerSelfDebitHookMPT(issue, amount, available); } -static TER -checkMPTAllowed( - ReadView const& view, - TxType txType, - Asset const& asset, - AccountID const& accountID, - std::uint8_t depth = 0) -{ - if (!asset.holds()) - return tesSUCCESS; - - auto const& issuanceID = asset.get().getMptID(); - auto const validTx = txType == ttAMM_CREATE || txType == ttAMM_DEPOSIT || - txType == ttAMM_WITHDRAW || txType == ttOFFER_CREATE || txType == ttCHECK_CREATE || - txType == ttCHECK_CASH || txType == ttPAYMENT; - XRPL_ASSERT(validTx, "xrpl::checkMPTAllowed : all MPT tx or DEX"); - if (!validTx) - return tefINTERNAL; // LCOV_EXCL_LINE - - auto const& issuer = asset.getIssuer(); - if (!view.exists(keylet::account(issuer))) - return tecNO_ISSUER; // LCOV_EXCL_LINE - - auto const issuanceKey = keylet::mptIssuance(issuanceID); - auto const issuanceSle = view.read(issuanceKey); - if (!issuanceSle) - return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE - - if (issuanceSle->isFlag(lsfMPTLocked)) - return tecLOCKED; // LCOV_EXCL_LINE - // Offer crossing and Payment - if (!issuanceSle->isFlag(lsfMPTCanTrade)) - return tecNO_PERMISSION; - - if (accountID != issuer) - { - if (!issuanceSle->isFlag(lsfMPTCanTransfer)) - return tecNO_PERMISSION; - - auto const mptSle = view.read(keylet::mptoken(issuanceKey.key, accountID)); - // Allow to succeed since some tx create MPToken if it doesn't exist. - // Tx's have their own check for missing MPToken. - if (!mptSle) - return tesSUCCESS; - - if (mptSle->isFlag(lsfMPTLocked)) - return tecLOCKED; - - // Post-fixCleanup3_2_0: vault shares inherit the underlying - // asset's checks here too. Without this, a share could be - // placed on the AMM, in an Offer, or in a Check even after - // the issuer has restricted the underlying. Mirrors the - // canTransfer / canTrade inheritance for path-find-adjacent - // operations that don't go through canTransfer directly. - if (view.rules().enabled(fixCleanup3_2_0) && - issuanceSle->isFieldPresent(sfReferenceHolding)) - { - // Defensive depth bound on the inheritance recursion. - // Reachable only post-fixCleanup3_2_0 and unreachable in - // practice (vault-of-vault-shares forbidden at VaultCreate). - if (depth >= kMaxAssetCheckDepth) - { - // LCOV_EXCL_START - UNREACHABLE("xrpl::MPTokenHelpers::checkMPTAllowed : reached asset check depth"); - return tecINTERNAL; - // LCOV_EXCL_STOP - } - auto const sleHolding = - view.read(keylet::unchecked(issuanceSle->getFieldH256(sfReferenceHolding))); - if (!sleHolding) - return tefINTERNAL; // LCOV_EXCL_LINE - - return checkMPTAllowed( - view, txType, assetOfHolding(*issuanceSle, *sleHolding), accountID, depth + 1); - } - } - - return tesSUCCESS; -} - -TER -checkMPTTxAllowed( - ReadView const& view, - TxType txType, - Asset const& asset, - AccountID const& accountID) -{ - // use isDEXAllowed for payment/offer crossing - XRPL_ASSERT(txType != ttPAYMENT, "xrpl::checkMPTTxAllowed : not payment"); - return checkMPTAllowed(view, txType, asset, accountID); -} - } // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index 71019761ed..7191456868 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -55,6 +56,14 @@ isGlobalFrozen(ReadView const& view, Asset const& asset) [&](MPTIssue const& issue) { return isGlobalFrozen(view, issue); }); } +TER +checkGlobalFrozen(ReadView const& view, Asset const& asset) +{ + if (isGlobalFrozen(view, asset)) + return asset.holds() ? tecLOCKED : tecFROZEN; + return tesSUCCESS; +} + bool isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset) { @@ -62,6 +71,14 @@ isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& [&](auto const& issue) { return isIndividualFrozen(view, account, issue); }, asset.value()); } +TER +checkIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset) +{ + if (isIndividualFrozen(view, account, asset)) + return asset.holds() ? tecLOCKED : tecFROZEN; + return tesSUCCESS; +} + bool isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, std::uint8_t depth) { @@ -1105,6 +1122,13 @@ directSendNoFeeMPT( auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID); if (auto sle = view.peek(mptokenID)) { + if (view.rules().enabled(featureMPTokensV2)) + { + if ((*sle)[sfMPTAmount] > (std::numeric_limits::max() - amt)) + { + return tecINTERNAL; // LCOV_EXCL_LINE + } + } view.creditHookMPT(uSenderID, uReceiverID, saAmount, (*sle)[sfMPTAmount], available); (*sle)[sfMPTAmount] += amt; view.update(sle); diff --git a/src/libxrpl/protocol/STPathSet.cpp b/src/libxrpl/protocol/STPathSet.cpp index 35d5fd782b..cb70ac36fe 100644 --- a/src/libxrpl/protocol/STPathSet.cpp +++ b/src/libxrpl/protocol/STPathSet.cpp @@ -90,8 +90,12 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name) if (hasAccount) account = sit.get160(); - XRPL_ASSERT( - !(hasCurrency && hasMPT), "xrpl::STPathSet::STPathSet : not has Currency and MPT"); + if (hasCurrency && hasMPT) + { + JLOG(debugLog().error()) << "Bad path element MPT and Currency in pathset"; + Throw("bad path element: MPT and Currency"); + } + if (hasCurrency) asset = Currency::fromRaw(sit.get160()); @@ -101,7 +105,7 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name) if (hasIssuer) issuer = sit.get160(); - path.emplace_back(account, asset, issuer, hasCurrency); + path.emplace_back(account, asset, issuer, hasCurrency || hasMPT); } } } diff --git a/src/libxrpl/tx/invariants/AMMInvariant.cpp b/src/libxrpl/tx/invariants/AMMInvariant.cpp index e13684eca5..be2a803e93 100644 --- a/src/libxrpl/tx/invariants/AMMInvariant.cpp +++ b/src/libxrpl/tx/invariants/AMMInvariant.cpp @@ -45,7 +45,8 @@ ValidAMM::visitEntry( // AMM pool changed else if ( (type == ltRIPPLE_STATE && after->isFlag(lsfAMMNode)) || - (type == ltACCOUNT_ROOT && after->isFieldPresent(sfAMMID))) + (type == ltACCOUNT_ROOT && after->isFieldPresent(sfAMMID)) || + (type == ltMPTOKEN && after->isFlag(lsfMPTAMM))) { ammPoolChanged_ = true; } @@ -85,9 +86,9 @@ ValidAMM::finalizeVote(bool enforce, beast::Journal const& j) const { // LPTokens and the pool can not change on vote // LCOV_EXCL_START - JLOG(j.error()) << "AMMVote invariant failed: " << lptAMMBalanceBefore_.value_or(STAmount{}) - << " " << lptAMMBalanceAfter_.value_or(STAmount{}) << " " - << ammPoolChanged_; + JLOG(j.error()) << "Invariant failed: AMMVote failed, " + << lptAMMBalanceBefore_.value_or(STAmount{}) << " " + << lptAMMBalanceAfter_.value_or(STAmount{}) << " " << ammPoolChanged_; if (enforce) return false; // LCOV_EXCL_STOP @@ -103,7 +104,7 @@ ValidAMM::finalizeBid(bool enforce, beast::Journal const& j) const { // The pool can not change on bid // LCOV_EXCL_START - JLOG(j.error()) << "AMMBid invariant failed: pool changed"; + JLOG(j.error()) << "Invariant failed: AMMBid failed, pool changed"; if (enforce) return false; // LCOV_EXCL_STOP @@ -114,7 +115,7 @@ ValidAMM::finalizeBid(bool enforce, beast::Journal const& j) const (*lptAMMBalanceAfter_ > *lptAMMBalanceBefore_ || *lptAMMBalanceAfter_ <= beast::kZero)) { // LCOV_EXCL_START - JLOG(j.error()) << "AMMBid invariant failed: " << *lptAMMBalanceBefore_ << " " + JLOG(j.error()) << "Invariant failed: AMMBid failed, " << *lptAMMBalanceBefore_ << " " << *lptAMMBalanceAfter_; if (enforce) return false; @@ -134,7 +135,7 @@ ValidAMM::finalizeCreate( if (!ammAccount_) { // LCOV_EXCL_START - JLOG(j.error()) << "AMMCreate invariant failed: AMM object is not created"; + JLOG(j.error()) << "Invariant failed: AMMCreate failed, AMM object is not created"; if (enforce) return false; // LCOV_EXCL_STOP @@ -157,8 +158,8 @@ ValidAMM::finalizeCreate( if (!validBalances(amount, amount2, *lptAMMBalanceAfter_, ZeroAllowed::No) || ammLPTokens(amount, amount2, lptAMMBalanceAfter_->get()) != *lptAMMBalanceAfter_) { - JLOG(j.error()) << "AMMCreate invariant failed: " << amount << " " << amount2 << " " - << *lptAMMBalanceAfter_; + JLOG(j.error()) << "Invariant failed: AMMCreate failed, " << amount << " " << amount2 + << " " << *lptAMMBalanceAfter_; if (enforce) return false; } @@ -176,7 +177,7 @@ ValidAMM::finalizeDelete(bool enforce, TER res, beast::Journal const& j) const // LCOV_EXCL_START std::string const msg = (isTesSuccess(res)) ? "AMM object is not deleted on tesSUCCESS" : "AMM object is changed on tecINCOMPLETE"; - JLOG(j.error()) << "AMMDelete invariant failed: " << msg; + JLOG(j.error()) << "Invariant failed: AMMDelete failed, " << msg; if (enforce) return false; // LCOV_EXCL_STOP @@ -191,7 +192,7 @@ ValidAMM::finalizeDEX(bool enforce, beast::Journal const& j) const if (ammAccount_) { // LCOV_EXCL_START - JLOG(j.error()) << "AMM swap invariant failed: AMM object changed"; + JLOG(j.error()) << "Invariant failed: AMM swap failed, AMM object changed"; if (enforce) return false; // LCOV_EXCL_STOP @@ -232,10 +233,10 @@ ValidAMM::generalInvariant( }; if (!nonNegativeBalances || (!strongInvariantCheck && !weakInvariantCheck())) { - JLOG(j.error()) << "AMM " << tx.getTxnType() - << " invariant failed: " << tx.getHash(HashPrefix::TransactionId) << " " - << ammPoolChanged_ << " " << amount << " " << amount2 << " " - << poolProductMean << " " << lptAMMBalanceAfter_->getText() << " " + JLOG(j.error()) << "Invariant failed: AMM " << tx.getTxnType() << " " + << tx.getHash(HashPrefix::TransactionId) << " " << ammPoolChanged_ << " " + << amount << " " << amount2 << " " << poolProductMean << " " + << lptAMMBalanceAfter_->getText() << " " << ((*lptAMMBalanceAfter_ == beast::kZero) ? Number{1} : ((*lptAMMBalanceAfter_ - poolProductMean) / poolProductMean)); @@ -256,7 +257,7 @@ ValidAMM::finalizeDeposit( if (!ammAccount_) { // LCOV_EXCL_START - JLOG(j.error()) << "AMMDeposit invariant failed: AMM object is deleted"; + JLOG(j.error()) << "Invariant failed: AMMDeposit failed, AMM object is deleted"; if (enforce) return false; // LCOV_EXCL_STOP diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 2019e378e2..3e51b6f877 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -840,7 +840,7 @@ ValidClawback::finalize( [&](MPTIssue const& issue) { return accountHolds( view, - issuer, + holder, issue, FreezeHandling::IgnoreFreeze, AuthHandling::IgnoreAuth, diff --git a/src/libxrpl/tx/invariants/MPTInvariant.cpp b/src/libxrpl/tx/invariants/MPTInvariant.cpp index ecba0b6227..635af25b61 100644 --- a/src/libxrpl/tx/invariants/MPTInvariant.cpp +++ b/src/libxrpl/tx/invariants/MPTInvariant.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -442,11 +443,11 @@ ValidMPTPayment::finalize( { if (isTesSuccess(result)) { - bool const enforce = view.rules().enabled(featureMPTokensV2); + bool const invariantPasses = !view.rules().enabled(featureMPTokensV2); if (overflow_) { JLOG(j.fatal()) << "Invariant failed: OutstandingAmount overflow"; - return !enforce; + return invariantPasses; } auto const signedMax = static_cast(kMaxMpTokenAmount); @@ -464,7 +465,7 @@ ValidMPTPayment::finalize( JLOG(j.fatal()) << "Invariant failed: invalid OutstandingAmount balance " << data.outstanding[kIBefore] << " " << data.outstanding[kIAfter] << " " << data.mptAmount; - return !enforce; + return invariantPasses; } } } @@ -472,4 +473,147 @@ ValidMPTPayment::finalize( return true; } +void +ValidMPTTransfer::visitEntry( + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) +{ + // Record the before/after MPTAmount for each (issuanceID, account) pair + // so finalize() can determine whether a transfer actually occurred. + auto update = [&](SLE const& sle, bool isBefore) { + if (sle.getType() == ltMPTOKEN) + { + auto const issuanceID = sle[sfMPTokenIssuanceID]; + auto const account = sle[sfAccount]; + auto const amount = sle[sfMPTAmount]; + if (isBefore) + { + amount_[issuanceID][account].amtBefore = amount; + } + else + { + amount_[issuanceID][account].amtAfter = amount; + } + if (isDelete && isBefore) + { + deletedAuthorized_[sle.key()] = sle.isFlag(lsfMPTAuthorized); + } + } + }; + + if (before) + update(*before, true); + + if (after) + update(*after, false); +} + +bool +ValidMPTTransfer::isAuthorized( + ReadView const& view, + MPTID const& mptid, + AccountID const& holder, + bool reqAuth) const +{ + auto const key = keylet::mptoken(mptid, holder); + auto const it = deletedAuthorized_.find(key.key); + if (it != deletedAuthorized_.end()) + return !reqAuth || it->second; + return isTesSuccess(requireAuth(view, MPTIssue{mptid}, holder)); +} + +bool +ValidMPTTransfer::finalize( + STTx const& tx, + TER const, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) +{ + if (hasPrivilege(tx, OverrideFreeze)) + return true; + + // DEX transactions (AMM[Create,Deposit], cross-currency payments, offer creates) are + // subject to the MPTCanTrade flag in addition to the standard transfer rules. + // A payment is only DEX if it is a cross-currency payment. + auto const txnType = tx.getTxnType(); + auto const isDEX = [&] { + if (txnType == ttPAYMENT) + { + // A payment is cross-currency (and thus DEX) only if SendMax is present + // and its asset differs from the destination asset. + auto const amount = tx[sfAmount]; + return tx[~sfSendMax].value_or(amount).asset() != amount.asset(); + } + return txnType == ttAMM_CREATE || txnType == ttAMM_DEPOSIT || txnType == ttOFFER_CREATE; + }(); + + // Only enforce once MPTokensV2 is enabled to preserve consensus with non-V2 nodes. + // Log invariant failure error even if MPTokensV2 is disabled. + auto const invariantPasses = !view.rules().enabled(featureMPTokensV2); + + for (auto const& [mptID, values] : amount_) + { + std::uint16_t senders = 0; + std::uint16_t receivers = 0; + bool invalidTransfer = false; + auto const sleIssuance = view.read(keylet::mptIssuance(mptID)); + if (!sleIssuance) + { + continue; + } + + // These transactions are recovery/settlement paths. They may move an + // existing MPT position even after the issuer clears CanTransfer, so + // holders are not trapped in AMM, vault, or loan protocol accounts. + auto const waivesCanTransfer = txnType == ttAMM_WITHDRAW || + (view.rules().enabled(fixCleanup3_2_0) && + (txnType == ttVAULT_WITHDRAW || txnType == ttLOAN_BROKER_COVER_WITHDRAW || + txnType == ttLOAN_PAY)); + auto const canTransfer = sleIssuance->isFlag(lsfMPTCanTransfer) || waivesCanTransfer; + auto const canTrade = sleIssuance->isFlag(lsfMPTCanTrade); + auto const reqAuth = sleIssuance->isFlag(lsfMPTRequireAuth); + + for (auto const& [account, value] : values) + { + // Classify each account as a sender or receiver based on whether their MPTAmount + // decreased or increased. Count new MPToken holders (no amtBefore) as receivers. + // Skip deleted MPToken holders (amtAfter is nullopt); deletion requires zero balance. + if (value.amtAfter.has_value() && value.amtBefore.value_or(0) != *value.amtAfter) + { + if (!value.amtBefore.has_value() || *value.amtAfter > *value.amtBefore) + { + ++receivers; + } + else + { + ++senders; + } + + // Check once: if any involved account is frozen, the whole + // issuance transfer is considered frozen. Only need to check for + // frozen if there is a transfer of funds. + if (!invalidTransfer && + (isFrozen(view, account, MPTIssue{mptID}) || + !isAuthorized(view, mptID, account, reqAuth))) + { + invalidTransfer = true; + } + } + } + // A transfer between holders has occurred (senders > 0 && receivers > 0). + // Fail if the issuance is frozen, does not permit transfers, or — for + // DEX transactions — does not permit trading. + if ((invalidTransfer || !canTransfer || (isDEX && !canTrade)) && senders > 0 && + receivers > 0) + { + JLOG(j.fatal()) << "Invariant failed: invalid MPToken transfer between holders"; + return invariantPasses; + } + } + + return true; +} + } // namespace xrpl diff --git a/src/libxrpl/tx/paths/BookStep.cpp b/src/libxrpl/tx/paths/BookStep.cpp index 0391aa5f77..5cc2a987b8 100644 --- a/src/libxrpl/tx/paths/BookStep.cpp +++ b/src/libxrpl/tx/paths/BookStep.cpp @@ -1361,19 +1361,18 @@ BookStep::check(StrandContext const& ctx) const return terNO_RIPPLE; return std::nullopt; }, - [&](MPTIssue const& issue) -> std::optional { - // Check if can trade on DEX. - if (auto const ter = canTrade(view, book_.in); !isTesSuccess(ter)) - return ter; - if (auto const ter = canTrade(view, book_.out); !isTesSuccess(ter)) - return ter; - return std::nullopt; - }); + [&](MPTIssue const& issue) -> std::optional { return std::nullopt; }); if (err) return *err; } } + // Check if the offer can be traded on DEX. + if (auto const ter = canTrade(ctx.view, book_.in); !isTesSuccess(ter)) + return ter; + if (auto const ter = canTrade(ctx.view, book_.out); !isTesSuccess(ter)) + return ter; + return tesSUCCESS; } @@ -1384,12 +1383,22 @@ BookStep::rate( Asset const& asset, AccountID const& dstAccount) const { - auto const& issuer = asset.getIssuer(); - if (isXRP(issuer) || issuer == dstAccount) - return kParityRate; return asset.visit( - [&](Issue const&) { return transferRate(view, issuer); }, - [&](MPTIssue const& issue) { return transferRate(view, issue.getMptID()); }); + [&](Issue const& issue) -> Rate { + if (isXRP(issue.account) || issue.account == dstAccount) + return kParityRate; + return transferRate(view, issue.account); + }, + [&](MPTIssue const& mptIssue) -> Rate { + // For MPT, parity applies only when this asset is the final strand + // delivery AND the destination is the MPT issuer (holder → issuer, + // which is fee-free). Using strandDst_ alone is wrong because it + // incorrectly suppresses the fee when MPT is an intermediate or + // the in-side of a book that precedes the issuer's XRP receipt. + if (asset == strandDeliver_ && mptIssue.getIssuer() == dstAccount) + return kParityRate; + return transferRate(view, mptIssue.getMptID()); + }); }; template diff --git a/src/libxrpl/tx/paths/MPTEndpointStep.cpp b/src/libxrpl/tx/paths/MPTEndpointStep.cpp index 65f8409379..5595f8ea65 100644 --- a/src/libxrpl/tx/paths/MPTEndpointStep.cpp +++ b/src/libxrpl/tx/paths/MPTEndpointStep.cpp @@ -395,6 +395,9 @@ MPTEndpointPaymentStep::check(StrandContext const& ctx, std::shared_ptr const&) { + // The standard checks are all we can do because any remaining checks + // require the existence of a MPToken. Offer crossing does not + // require a pre-existing MPToken. return tesSUCCESS; } @@ -838,10 +841,17 @@ MPTEndpointStep::check(StrandContext const& ctx) const } // pure issue/redeem can't be frozen (issuer/holder) + // For the first step: check global freeze of the step's own asset. + // For the last step: check only the per-holder MPToken lock. + // Global freeze of the deliver asset is not checked here + // because MPT semantics allow issuer<->holder transfers even when globally + // locked — only holder-to-holder DEX paths are restricted. if (!(ctx.isLast && ctx.isFirst)) { auto const& account = ctx.isFirst ? src_ : dst_; - if (isFrozen(ctx.view, account, mptIssue_)) + bool const frozen = (ctx.isFirst && isGlobalFrozen(ctx.view, mptIssue_)) || + isIndividualFrozen(ctx.view, account, mptIssue_); + if (frozen) return terLOCKED; } diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index f8dbae66fc..cfc133b501 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -264,9 +264,10 @@ CheckCash::preclaim(PreclaimContext const& ctx) return tecLOCKED; } - if (auto const err = canTrade(ctx.view, value.asset()); !isTesSuccess(err)) + if (auto const err = canTransfer(ctx.view, issue, srcId, dstId); + !isTesSuccess(err)) { - JLOG(ctx.j.warn()) << "MPT DEX is not allowed."; + JLOG(ctx.j.warn()) << "MPT transfer is disabled."; return err; } diff --git a/src/libxrpl/tx/transactors/check/CheckCreate.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp index 6373d7e343..d2d84536de 100644 --- a/src/libxrpl/tx/transactors/check/CheckCreate.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -111,10 +111,10 @@ CheckCreate::preclaim(PreclaimContext const& ctx) { // The currency may not be globally frozen AccountID const& issuerId{sendMax.getIssuer()}; - if (isGlobalFrozen(ctx.view, sendMax.asset())) + if (auto const ter = checkGlobalFrozen(ctx.view, sendMax.asset()); !isTesSuccess(ter)) { - JLOG(ctx.j.warn()) << "Creating a check for frozen asset"; - return sendMax.asset().holds() ? tecLOCKED : tecFROZEN; + JLOG(ctx.j.warn()) << "Creating a check for frozen or locked asset"; + return ter; } auto const err = sendMax.asset().visit( [&](Issue const& issue) -> std::optional { @@ -153,9 +153,21 @@ CheckCreate::preclaim(PreclaimContext const& ctx) }, [&](MPTIssue const& issue) -> std::optional { if (srcId != issuerId && isFrozen(ctx.view, srcId, issue)) + { + JLOG(ctx.j.warn()) << "Creating a check for locked MPT."; return tecLOCKED; + } if (dstId != issuerId && isFrozen(ctx.view, dstId, issue)) + { + JLOG(ctx.j.warn()) << "Creating a check for locked MPT."; return tecLOCKED; + } + if (auto const ter = canTransfer(ctx.view, issue, srcId, dstId); + !isTesSuccess(ter)) + { + JLOG(ctx.j.warn()) << "MPT transfer is disabled."; + return ter; + } return std::nullopt; }); @@ -169,7 +181,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx) return tecEXPIRED; } - return canTrade(ctx.view, ctx.tx[sfSendMax].asset()); + return tesSUCCESS; } TER diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index 3e1fe97a76..60508eab85 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -120,11 +119,16 @@ AMMCreate::preclaim(PreclaimContext const& ctx) } // Globally or individually frozen - if (isFrozen(ctx.view, accountID, amount.asset()) || - isFrozen(ctx.view, accountID, amount2.asset())) + if (auto const ter = checkFrozen(ctx.view, accountID, amount.asset()); !isTesSuccess(ter)) + { - JLOG(ctx.j.debug()) << "AMM Instance: involves frozen asset."; - return tecFROZEN; + JLOG(ctx.j.debug()) << "AMM Instance: involves frozen or locked asset."; + return ter; + } + if (auto const ter = checkFrozen(ctx.view, accountID, amount2.asset()); !isTesSuccess(ter)) + { + JLOG(ctx.j.debug()) << "AMM Instance: involves frozen or locked asset."; + return ter; } auto noDefaultRipple = [](ReadView const& view, Asset const& asset) { @@ -191,10 +195,10 @@ AMMCreate::preclaim(PreclaimContext const& ctx) return terADDRESS_COLLISION; } - if (auto const ter = checkMPTTxAllowed(ctx.view, ttAMM_CREATE, amount.asset(), accountID); + if (auto const ter = canMPTTradeAndTransfer(ctx.view, amount.asset(), accountID, accountID); !isTesSuccess(ter)) return ter; - if (auto const ter = checkMPTTxAllowed(ctx.view, ttAMM_CREATE, amount2.asset(), accountID); + if (auto const ter = canMPTTradeAndTransfer(ctx.view, amount2.asset(), accountID, accountID); !isTesSuccess(ter)) return ter; @@ -301,22 +305,14 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou // Authorize MPT return amount.asset().visit( [&](MPTIssue const& issue) -> TER { - // Authorize MPT auto const& mptIssue = issue; auto const& mptID = mptIssue.getMptID(); - std::uint32_t flags = lsfMPTAMM; - if (auto const err = - requireAuth(ctx.view(), mptIssue, accountId, AuthType::WeakAuth); + // Implicitly authorize MPT asset for AMM pseudo-account. + std::uint32_t const flags = lsfMPTAMM | lsfMPTAuthorized; + if (auto const err = requireAuth(sb, mptIssue, accountId, AuthType::WeakAuth); !isTesSuccess(err)) { - if (err == tecNO_AUTH) - { - flags |= lsfMPTAuthorized; - } - else - { - return err; - } + return err; } if (auto const err = createMPToken(sb, mptID, accountId, flags); !isTesSuccess(err)) diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index 86cd52300d..f45529f617 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -267,12 +266,12 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) return ter; } - if (isFrozen(ctx.view, accountID, asset)) + if (auto const ter = checkFrozen(ctx.view, accountID, asset); !isTesSuccess(ter)) { - JLOG(ctx.j.debug()) << "AMM Deposit: account or currency is frozen, " + JLOG(ctx.j.debug()) << "AMM Deposit: account or currency is frozen or locked, " << to_string(accountID) << " " << to_string(asset); - return tecFROZEN; + return ter; } return tesSUCCESS; @@ -304,18 +303,20 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) // LCOV_EXCL_STOP } // AMM account or currency frozen - if (isFrozen(ctx.view, ammAccountID, amount->asset())) + if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset()); + !isTesSuccess(ter)) { - JLOG(ctx.j.debug()) - << "AMM Deposit: AMM account or currency is frozen, " << to_string(accountID); - return tecFROZEN; + JLOG(ctx.j.debug()) << "AMM Deposit: AMM account or currency is frozen or locked, " + << to_string(accountID); + return ter; } // Account frozen - if (isIndividualFrozen(ctx.view, accountID, amount->asset())) + if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset()); + !isTesSuccess(ter)) { - JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen, " << to_string(accountID) - << " " << to_string(amount->asset()); - return tecFROZEN; + JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen or locked, " + << to_string(accountID) << " " << to_string(amount->asset()); + return ter; } if (checkBalance) { @@ -368,10 +369,10 @@ AMMDeposit::preclaim(PreclaimContext const& ctx) } } - if (auto const ter = checkMPTTxAllowed(ctx.view, ttAMM_DEPOSIT, ctx.tx[sfAsset], accountID); + if (auto const ter = canMPTTradeAndTransfer(ctx.view, ctx.tx[sfAsset], accountID, accountID); !isTesSuccess(ter)) return ter; - if (auto const ter = checkMPTTxAllowed(ctx.view, ttAMM_DEPOSIT, ctx.tx[sfAsset2], accountID); + if (auto const ter = canMPTTradeAndTransfer(ctx.view, ctx.tx[sfAsset2], accountID, accountID); !isTesSuccess(ter)) return ter; diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index a4fd1518f5..352f637f2f 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -26,7 +25,6 @@ #include #include #include -#include #include #include @@ -242,24 +240,21 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) return ter; } // AMM account or currency frozen - if (isFrozen(ctx.view, ammAccountID, amount->asset())) + if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset()); + !isTesSuccess(ter)) { - JLOG(ctx.j.debug()) - << "AMM Withdraw: AMM account or currency is frozen, " << to_string(accountID); - return tecFROZEN; + JLOG(ctx.j.debug()) << "AMM Withdraw: AMM account or currency is frozen or locked, " + << to_string(accountID); + return ter; } // Account frozen - if (isIndividualFrozen(ctx.view, accountID, amount->asset())) - { - JLOG(ctx.j.debug()) << "AMM Withdraw: account is frozen, " << to_string(accountID) - << " " << to_string(amount->asset()); - return tecFROZEN; - } - - if (auto const ter = - checkMPTTxAllowed(ctx.view, ttAMM_WITHDRAW, amount->asset(), accountID); + if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset()); !isTesSuccess(ter)) + { + JLOG(ctx.j.debug()) << "AMM Withdraw: account is frozen or locked, " + << to_string(accountID) << " " << to_string(amount->asset()); return ter; + } } return tesSUCCESS; }; @@ -635,10 +630,6 @@ AMMWithdraw::withdraw( auto const balanceAdj = isIssue ? std::max(priorBalance, balance.xrp()) : priorBalance; if (balanceAdj < reserve) return tecINSUFFICIENT_RESERVE; - - // Update owner count. - if (!isIssue) - adjustOwnerCount(view, sleAccount, 1, journal); } return tesSUCCESS; }; diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index ed9eb1255d..8bf69d25c0 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -181,11 +181,15 @@ OfferCreate::preclaim(PreclaimContext const& ctx) auto viewJ = ctx.registry.get().getJournal("View"); - if (isGlobalFrozen(ctx.view, saTakerPays.asset()) || - isGlobalFrozen(ctx.view, saTakerGets.asset())) + if (auto const ter = checkGlobalFrozen(ctx.view, saTakerPays.asset()); !isTesSuccess(ter)) { - JLOG(ctx.j.debug()) << "Offer involves frozen asset"; - return tecFROZEN; + JLOG(ctx.j.debug()) << "Offer involves frozen or locked asset"; + return ter; + } + if (auto const ter = checkGlobalFrozen(ctx.view, saTakerGets.asset()); !isTesSuccess(ter)) + { + JLOG(ctx.j.debug()) << "Offer involves frozen or locked asset"; + return ter; } // Allow unfunded MPT for issuer (OutstandingAmount >= MaximumAmount) @@ -320,7 +324,13 @@ OfferCreate::checkAcceptAsset( [&](MPTIssue const& issue) -> TER { // WeakAuth - don't check if MPToken exists since it's created // if needed. - return requireAuth(view, issue, id, AuthType::WeakAuth); + if (auto const ter = requireAuth(view, issue, id, AuthType::WeakAuth); + !isTesSuccess(ter)) + { + return ter; + } + + return checkFrozen(view, id, issue); }); } diff --git a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp index 382ea7d40e..9b0c8d2b56 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp @@ -133,8 +133,9 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) // Can't unauthorize the pseudo-accounts because they are implicitly // always authorized. No need to amendment gate since Vault and LoanBroker - // can only be created if the Vault amendment is enabled. - if (isPseudoAccount(ctx.view, *holderID, {&sfVaultID, &sfLoanBrokerID})) + // can only be created if the Vault amendment is enabled; AMM with MPToken asset + // can only be created if MPTokensV2 is enabled. + if (isPseudoAccount(ctx.view, *holderID, {&sfVaultID, &sfLoanBrokerID, &sfAMMID})) return tecNO_PERMISSION; return tesSUCCESS; diff --git a/src/test/app/AMMExtendedMPT_test.cpp b/src/test/app/AMMExtendedMPT_test.cpp index cf1210901f..e6dcc95733 100644 --- a/src/test/app/AMMExtendedMPT_test.cpp +++ b/src/test/app/AMMExtendedMPT_test.cpp @@ -3114,10 +3114,8 @@ private: btc.set({.holder = bob, .flags = tfMPTLock}); { - // different from IOU. The offer is created but not crossed. - env(offer(bob, btc(5), XRP(25))); + env(offer(bob, btc(5), XRP(25)), Ter(tecLOCKED)); env.close(); - BEAST_EXPECT(expectOffers(env, bob, 1, {{{btc(5), XRP(25)}}})); BEAST_EXPECT(ammAlice.expectBalances(XRP(500), btc(105), ammAlice.tokens())); } @@ -3220,7 +3218,7 @@ private: btc.set({.flags = tfMPTLock}); // assets can't be bought on the market - AMM const ammA3(env, a3, btc(1), XRP(1), Ter(tecFROZEN)); + AMM const ammA3(env, a3, btc(1), XRP(1), Ter(tecLOCKED)); // direct issues can be sent env(pay(g1, a2, btc(1))); diff --git a/src/test/app/AMMMPT_test.cpp b/src/test/app/AMMMPT_test.cpp index cfb8edc60b..31b54ceee0 100644 --- a/src/test/app/AMMMPT_test.cpp +++ b/src/test/app/AMMMPT_test.cpp @@ -144,7 +144,7 @@ private: .pay = 30'000, .flags = tfMPTCanLock | kMptDexFlags}); usd.set({.flags = tfMPTLock}); - AMM const ammAliceFail(env, alice_, XRP(10'000), usd(10'000), Ter(tecFROZEN)); + AMM const ammAliceFail(env, alice_, XRP(10'000), usd(10'000), Ter(tecLOCKED)); usd.set({.flags = tfMPTUnlock}); AMM const ammAlice(env, alice_, XRP(10'000), usd(10'000)); } @@ -348,7 +348,7 @@ private: .pay = 30'000, .flags = tfMPTCanLock | kMptDexFlags}); btc.set({.flags = tfMPTLock}); - AMM const ammAlice(env, alice_, USD(10'000), btc(10'000), Ter(tecFROZEN)); + AMM const ammAlice(env, alice_, USD(10'000), btc(10'000), Ter(tecLOCKED)); BEAST_EXPECT(!ammAlice.ammExists()); } @@ -365,7 +365,7 @@ private: btc.set({.holder = alice_, .flags = tfMPTLock}); // alice's token is locked - AMM const ammAlice(env, alice_, USD(10'000), btc(10'000), Ter(tecFROZEN)); + AMM const ammAlice(env, alice_, USD(10'000), btc(10'000), Ter(tecLOCKED)); BEAST_EXPECT(!ammAlice.ammExists()); // bob can create @@ -693,15 +693,15 @@ private: btc.set({.flags = tfMPTLock}); ammAlice.deposit( - carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); ammAlice.deposit( - carol_, USD(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, USD(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); - ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecFROZEN)); + ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecLOCKED)); ammAlice.deposit( - carol_, USD(100), btc(100), std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, USD(100), btc(100), std::nullopt, std::nullopt, Ter(tecLOCKED)); } // Individually lock MPT or freeze IOU (AMM) with IOU/MPT AMM @@ -722,20 +722,19 @@ private: // Carol can not deposit locked mpt ammAlice.deposit( - carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); - ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecFROZEN)); + ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecLOCKED)); if (!features[featureAMMClawback]) { - ammAlice.deposit( - carol_, USD(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); + ammAlice.deposit(carol_, USD(100), std::nullopt, std::nullopt, std::nullopt); } else { - // Carol can not deposit non-forzen token either + // Carol can not deposit non-frozen token either ammAlice.deposit( - carol_, USD(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, USD(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); } // Alice can deposit because she's not individually locked @@ -778,9 +777,9 @@ private: ammAlice.deposit(carol_, USD(100), std::nullopt, std::nullopt, std::nullopt); // Can not deposit locked token - ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecFROZEN)); + ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecLOCKED)); ammAlice.deposit( - carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); // Unlock AMM MPT btc.set({.holder = ammAlice.ammAccount(), .flags = tfMPTUnlock}); @@ -812,10 +811,10 @@ private: // Carol's BTC is locked btc.set({.holder = carol_, .flags = tfMPTLock}); ammAlice.deposit( - carol_, usd(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, usd(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); ammAlice.deposit( - carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); // Unlock carol's BTC btc.set({.holder = carol_, .flags = tfMPTUnlock}); @@ -831,9 +830,9 @@ private: ammAlice.deposit(carol_, usd(100), std::nullopt, std::nullopt, std::nullopt); // Can not deposit locked token BTC - ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecFROZEN)); + ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecLOCKED)); ammAlice.deposit( - carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, btc(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); // Unlock AMM MPT BTC btc.set({.holder = ammAlice.ammAccount(), .flags = tfMPTUnlock}); @@ -848,9 +847,9 @@ private: ammAlice.deposit(carol_, btc(100), std::nullopt, std::nullopt, std::nullopt); // Can not deposit locked token USD - ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecFROZEN)); + ammAlice.deposit(carol_, 1'000, std::nullopt, std::nullopt, Ter(tecLOCKED)); ammAlice.deposit( - carol_, usd(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecFROZEN)); + carol_, usd(100), std::nullopt, std::nullopt, std::nullopt, Ter(tecLOCKED)); // Unlock AMM MPT USD usd.set({.holder = ammAlice.ammAccount(), .flags = tfMPTUnlock}); @@ -904,7 +903,7 @@ private: AMM amm(env, gw, XRP(10'000), btc(10'000)); - amm.deposit({.account = alice, .asset1In = btc(10), .err = Ter(tecNO_PERMISSION)}); + amm.deposit({.account = alice, .asset1In = btc(10), .err = Ter(tecNO_AUTH)}); } // Insufficient XRP balance @@ -2246,23 +2245,22 @@ private: Env env{*this}; env.fund(XRP(30'000), gw_, alice_); env.close(); - auto btcm = MPTTester( + auto btc = MPTTester( {.env = env, .issuer = gw_, .holders = {alice_}, .pay = 30'000, - .flags = tfMPTCanTrade, + .flags = kMptDexFlags, + .mutableFlags = tmfMPTCanMutateCanTransfer, .authHolder = true}); - MPT const btc = btcm; AMM amm(env, gw_, XRP(10'000), btc(10'000)); + amm.deposit(DepositArg{.account = alice_, .asset1In = XRP(200), .asset2In = btc(200)}); + // Allow to withdraw if transfer is disabled + btc.set({.mutableFlags = tmfMPTClearCanTransfer}); amm.withdraw( - WithdrawArg{ - .account = alice_, - .asset1Out = btc(100), - .assets = {{XRP, btc}}, - .err = Ter(tecNO_PERMISSION)}); + WithdrawArg{.account = alice_, .asset1Out = btc(100), .assets = {{XRP, btc}}}); } // Globally locked MPT @@ -2283,8 +2281,8 @@ private: btc.set({.flags = tfMPTLock}); ammAlice.withdraw( - alice_, MPT(ammAlice[1])(100), std::nullopt, std::nullopt, Ter(tecFROZEN)); - ammAlice.withdraw(alice_, 1'000, std::nullopt, std::nullopt, Ter(tecFROZEN)); + alice_, MPT(ammAlice[1])(100), std::nullopt, std::nullopt, Ter(tecLOCKED)); + ammAlice.withdraw(alice_, 1'000, std::nullopt, std::nullopt, Ter(tecLOCKED)); // can single withdraw the other asset ammAlice.withdraw({.account = alice_, .asset1Out = XRP(100)}); @@ -2312,8 +2310,8 @@ private: // Alice's BTC is locked btc.set({.holder = alice_, .flags = tfMPTLock}); - ammAlice.withdraw(alice_, 1000, std::nullopt, std::nullopt, Ter(tecFROZEN)); - ammAlice.withdraw(alice_, btc(100), std::nullopt, std::nullopt, Ter(tecFROZEN)); + ammAlice.withdraw(alice_, 1000, std::nullopt, std::nullopt, Ter(tecLOCKED)); + ammAlice.withdraw(alice_, btc(100), std::nullopt, std::nullopt, Ter(tecLOCKED)); // can withdraw the other asset ammAlice.withdraw(alice_, usd(100), std::nullopt, std::nullopt); @@ -2341,8 +2339,8 @@ private: // Alice's BTC is locked btc.set({.holder = alice_, .flags = tfMPTLock}); - ammAlice.withdraw(alice_, 1'000, std::nullopt, std::nullopt, Ter(tecFROZEN)); - ammAlice.withdraw(alice_, btc(100), std::nullopt, std::nullopt, Ter(tecFROZEN)); + ammAlice.withdraw(alice_, 1'000, std::nullopt, std::nullopt, Ter(tecLOCKED)); + ammAlice.withdraw(alice_, btc(100), std::nullopt, std::nullopt, Ter(tecLOCKED)); // can still single withdraw the unlocked other asset ammAlice.withdraw(alice_, USD(100), std::nullopt, std::nullopt); @@ -2361,8 +2359,8 @@ private: ammAlice.withdraw(alice_, USD(100), std::nullopt, std::nullopt); // Can not withdraw locked token BTC - ammAlice.withdraw(alice_, 1'000, std::nullopt, std::nullopt, Ter(tecFROZEN)); - ammAlice.withdraw(alice_, btc(100), std::nullopt, std::nullopt, Ter(tecFROZEN)); + ammAlice.withdraw(alice_, 1'000, std::nullopt, std::nullopt, Ter(tecLOCKED)); + ammAlice.withdraw(alice_, btc(100), std::nullopt, std::nullopt, Ter(tecLOCKED)); // Unlock AMM MPT btc.set({.holder = ammAlice.ammAccount(), .flags = tfMPTUnlock}); @@ -6886,33 +6884,33 @@ private: cb(amm, btc); }; - // Deposit two assets, one of which is frozen, - // then we should get tecFROZEN error. + // Deposit two assets, one of which is locked, + // then we should get tecLOCKED error. { Env env(*this); testAMMDeposit(env, [&](AMM& amm, MPTTester& btc) { - amm.deposit(alice_, btc(100), XRP(100), std::nullopt, tfTwoAsset, Ter(tecFROZEN)); + amm.deposit(alice_, btc(100), XRP(100), std::nullopt, tfTwoAsset, Ter(tecLOCKED)); }); } - // Deposit one asset, which is the frozen token, - // then we should get tecFROZEN error. + // Deposit one asset, which is the locked token, + // then we should get tecLOCKED error. { Env env(*this); testAMMDeposit(env, [&](AMM& amm, MPTTester& btc) { amm.deposit( - alice_, btc(100), std::nullopt, std::nullopt, tfSingleAsset, Ter(tecFROZEN)); + alice_, btc(100), std::nullopt, std::nullopt, tfSingleAsset, Ter(tecLOCKED)); }); } // Deposit one asset which is not the frozen token, - // but the other asset is frozen. We should get tecFROZEN error + // but the other asset is frozen. We should get tecLOCKED error // when feature AMMClawback is enabled. { Env env(*this); testAMMDeposit(env, [&](AMM& amm, MPTTester& btc) { amm.deposit( - alice_, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, Ter(tecFROZEN)); + alice_, XRP(100), std::nullopt, std::nullopt, tfSingleAsset, Ter(tecLOCKED)); }); } } diff --git a/src/test/app/CheckMPT_test.cpp b/src/test/app/CheckMPT_test.cpp index a7fef5043c..1ca24051dd 100644 --- a/src/test/app/CheckMPT_test.cpp +++ b/src/test/app/CheckMPT_test.cpp @@ -1860,10 +1860,10 @@ class CheckMPT_test : public beast::unit_test::Suite // Use offers to automatically create MPT. MPT const oF4 = gw1["OF4"]; gw1.set(oF4, tfMPTLock); - env(offer(gw1, XRP(92), oF4(92)), Ter(tecFROZEN)); + env(offer(gw1, XRP(92), oF4(92)), Ter(tecLOCKED)); env.close(); BEAST_EXPECT(env.le(keylet::mptoken(oF4, alice)) == nullptr); - env(offer(alice, oF4(92), XRP(92)), Ter(tecFROZEN)); + env(offer(alice, oF4(92), XRP(92)), Ter(tecLOCKED)); env.close(); // No one's owner count should have changed. @@ -1951,10 +1951,10 @@ class CheckMPT_test : public beast::unit_test::Suite // Use offers to automatically create MPT. MPT const oF4 = gw1["OF4"]; gw1.set(oF4, tfMPTLock); - env(offer(alice, XRP(91), oF4(91)), Ter(tecFROZEN)); + env(offer(alice, XRP(91), oF4(91)), Ter(tecLOCKED)); env.close(); BEAST_EXPECT(env.le(keylet::mptoken(oF4, alice)) == nullptr); - env(offer(bob, oF4(91), XRP(91)), Ter(tecFROZEN)); + env(offer(bob, oF4(91), XRP(91)), Ter(tecLOCKED)); env.close(); // No one's owner count should have changed. diff --git a/src/test/app/ClawbackMPT_test.cpp b/src/test/app/ClawbackMPT_test.cpp new file mode 100644 index 0000000000..299e63d028 --- /dev/null +++ b/src/test/app/ClawbackMPT_test.cpp @@ -0,0 +1,435 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace xrpl { + +class ClawbackMPT_test : public beast::unit_test::Suite +{ + static std::uint32_t + ticketCount(test::jtx::Env const& env, test::jtx::Account const& acct) + { + std::uint32_t ret{0}; + if (auto const sleAcct = env.le(acct)) + ret = sleAcct->at(~sfTicketCount).value_or(0); + return ret; + } + + void + testValidation(FeatureBitset features) + { + testcase("Validation"); + using namespace test::jtx; + + // MPT clawback fails when featureMPTokensV1 is disabled + { + Env env(*this, features - featureMPTokensV1); + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(1000), alice, bob); + env.close(); + + auto const mpt = xrpl::test::jtx::MPT(alice.name(), makeMptID(env.seq(alice), alice)); + + env(claw(alice, mpt(5), bob), Ter(temDISABLED)); + env.close(); + } + + // MPT clawback fails when tfMPTCanClawback is not set on the issuance + { + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create({.ownerCount = 1, .holderCount = 0}); + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 100); + + mptAlice.claw(alice, bob, 10, tecNO_PERMISSION); + } + + // Test preflight validation failures + { + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(1000), alice, bob); + env.close(); + + auto const mpt = xrpl::test::jtx::MPT(alice.name(), makeMptID(env.seq(alice), alice)); + + // fails due to invalid flag + env(claw(alice, mpt(5), bob), Txflags(0x00008000), Ter(temINVALID_FLAG)); + env.close(); + + // fails due to zero amount + env(claw(alice, mpt(0), bob), Ter(temBAD_AMOUNT)); + env.close(); + + // fails due to negative amount + env(claw(alice, mpt(-1), bob), Ter(temBAD_AMOUNT)); + env.close(); + + // fails when holder is not specified + env(claw(alice, mpt(5)), Ter(temMALFORMED)); + env.close(); + + // fails when issuer and holder are the same account + env(claw(alice, mpt(5), alice), Ter(temMALFORMED)); + env.close(); + } + + // Test preclaim failures + { + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + auto const fakeMpt = + xrpl::test::jtx::MPT(alice.name(), makeMptID(env.seq(alice), alice)); + + // clawback fails when the issuance does not exist + env(claw(alice, fakeMpt(5), bob), Ter(tecOBJECT_NOT_FOUND)); + env.close(); + + mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback}); + + // clawback fails when bob has no MPToken + mptAlice.claw(alice, bob, 5, tecOBJECT_NOT_FOUND); + + mptAlice.authorize({.account = bob}); + + // clawback fails because bob's balance is 0 + mptAlice.claw(alice, bob, 5, tecINSUFFICIENT_FUNDS); + } + } + + void + testPermission(FeatureBitset features) + { + testcase("Permission"); + using namespace test::jtx; + + // Clawing back from a non-existent account fails + { + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + // bob is not funded and does not exist + MPTTester mptAlice(env, alice, MPTInit{}); + mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback}); + + mptAlice.claw(alice, bob, 5, terNO_ACCOUNT); + } + + // A non-issuer cannot claw back MPT + { + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const cindy{"cindy"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + env.fund(XRP(1000), cindy); + env.close(); + + mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback}); + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 1000); + + // cindy fails to claw because she is not the issuer + mptAlice.claw(cindy, bob, 200, tecNO_PERMISSION); + } + } + + void + testEnabled(FeatureBitset features) + { + testcase("Enable clawback"); + using namespace test::jtx; + + // Test that alice is able to successfully clawback MPT from bob + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback}); + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 1000); + + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 1000)); + + // alice claws back 200 tokens from bob + mptAlice.claw(alice, bob, 200); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 800)); + + // alice claws back remaining 800 tokens + mptAlice.claw(alice, bob, 800); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 0)); + } + + void + testMultiIssuance(FeatureBitset features) + { + testcase("Multi issuance"); + using namespace test::jtx; + + // Two issuers each issue their own MPT to cindy. + // Clawback from one does not affect the other. + { + Env env(*this, features); + + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const cindy{"cindy"}; + + MPTTester mptAlice(env, alice, {.holders = {cindy}}); + mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback}); + mptAlice.authorize({.account = cindy}); + mptAlice.pay(alice, cindy, 1000); + + MPTTester mptBob(env, bob, MPTInit{}); // cindy already funded by mptAlice + mptBob.create({.ownerCount = 1, .flags = tfMPTCanClawback}); + mptBob.authorize({.account = cindy}); + mptBob.pay(bob, cindy, 1000); + + BEAST_EXPECT(mptAlice.checkMPTokenAmount(cindy, 1000)); + BEAST_EXPECT(mptBob.checkMPTokenAmount(cindy, 1000)); + + // alice claws back 200 from cindy, bob's issuance is unaffected + mptAlice.claw(alice, cindy, 200); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(cindy, 800)); + BEAST_EXPECT(mptBob.checkMPTokenAmount(cindy, 1000)); + + // bob claws back 600 from cindy, alice's issuance is unaffected + mptBob.claw(bob, cindy, 600); + BEAST_EXPECT(mptBob.checkMPTokenAmount(cindy, 400)); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(cindy, 800)); + } + + // One issuer issues MPT to two different holders. + // Clawback from one holder does not affect the other. + { + Env env(*this, features); + + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const cindy{"cindy"}; + + MPTTester mptAlice(env, alice, {.holders = {bob, cindy}}); + mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback}); + mptAlice.authorize({.account = bob}); + mptAlice.authorize({.account = cindy}); + mptAlice.pay(alice, bob, 600); + mptAlice.pay(alice, cindy, 1000); + + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 600)); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(cindy, 1000)); + + // alice claws back 500 from bob, cindy's balance is unchanged + mptAlice.claw(alice, bob, 500); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 100)); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(cindy, 1000)); + + // alice claws back 300 from cindy, bob's balance is unchanged + mptAlice.claw(alice, cindy, 300); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(cindy, 700)); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 100)); + } + } + + void + testZeroBalanceAfterClawback(FeatureBitset features) + { + testcase("Zero balance after clawback"); + using namespace test::jtx; + + // After clawback reduces balance to zero, the MPToken object + // still exists (unlike IOU trustlines which are deleted). + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback}); + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 1000); + + BEAST_EXPECT(ownerCount(env, bob) == 1); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 1000)); + + // alice claws back the full amount + mptAlice.claw(alice, bob, 1000); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 0)); + + // bob still holds the MPToken object (balance 0, not deleted) + BEAST_EXPECT(ownerCount(env, bob) == 1); + } + + void + testLockedMPT(FeatureBitset features) + { + testcase("Locked MPT"); + using namespace test::jtx; + + // Test that globally locked MPT can still be clawed back + { + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock | tfMPTCanClawback}); + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 1000); + + // globally lock the issuance + mptAlice.set({.account = alice, .flags = tfMPTLock}); + + // clawback succeeds despite global lock + mptAlice.claw(alice, bob, 200); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 800)); + } + + // Test that individually locked MPT can still be clawed back + { + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanLock | tfMPTCanClawback}); + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 1000); + + // individually lock bob's MPToken + mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock}); + + // clawback succeeds despite individual lock + mptAlice.claw(alice, bob, 200); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 800)); + } + } + + void + testAmountExceedsAvailable(FeatureBitset features) + { + testcase("Amount exceeds available"); + using namespace test::jtx; + + // When alice tries to claw back more than bob holds, + // only the available balance is clawed back + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback}); + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 1000); + + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 1000)); + + // alice tries to claw back 2000, but bob only has 1000 + mptAlice.claw(alice, bob, 2000); + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 0)); + BEAST_EXPECT(mptAlice.checkMPTokenOutstandingAmount(0)); + + // MPToken object still exists with 0 balance + BEAST_EXPECT(ownerCount(env, bob) == 1); + } + + void + testTickets(FeatureBitset features) + { + testcase("Tickets"); + using namespace test::jtx; + + // Tests MPT clawback using tickets + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create({.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanClawback}); + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 100); + + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 100)); + + // alice creates 10 tickets + std::uint32_t ticketCnt = 10; + std::uint32_t aliceTicketSeq{env.seq(alice) + 1}; + env(ticket::create(alice, ticketCnt)); + env.close(); + std::uint32_t const aliceSeq{env.seq(alice)}; + BEAST_EXPECT(ticketCount(env, alice) == ticketCnt); + BEAST_EXPECT(ownerCount(env, alice) == ticketCnt + 1); // tickets + issuance + + while (ticketCnt > 0) + { + // alice claws back 5 tokens using a ticket + env(claw(alice, mptAlice.mpt(5), bob), ticket::Use(aliceTicketSeq++)); + env.close(); + + ticketCnt--; + BEAST_EXPECT(ticketCount(env, alice) == ticketCnt); + } + + // alice clawed back 50 tokens total, 50 remain + BEAST_EXPECT(mptAlice.checkMPTokenAmount(bob, 50)); + + // account sequence numbers did not advance + BEAST_EXPECT(env.seq(alice) == aliceSeq); + } + + void + testWithFeats(FeatureBitset features) + { + testValidation(features); + testPermission(features); + testEnabled(features); + testMultiIssuance(features); + testZeroBalanceAfterClawback(features); + testLockedMPT(features); + testAmountExceedsAvailable(features); + testTickets(features); + } + +public: + void + run() override + { + using namespace test::jtx; + FeatureBitset const all{testableAmendments()}; + testWithFeats(all); + } +}; + +BEAST_DEFINE_TESTSUITE(ClawbackMPT, app, xrpl); +} // namespace xrpl diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 5cb872f291..6fd1904992 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -4313,6 +4313,189 @@ class Invariants_test : public beast::unit_test::Suite return true; }); } + + // Invalid transfer + std::array, 3> const invalidTransferTests = { + std::make_pair(ttAMM_WITHDRAW, false), + std::make_pair(ttPAYMENT, false), + std::make_pair(ttPAYMENT, true)}; + for (auto const enabled : {true, false}) + { + for (auto const& [tx, crossCurrencyPayment] : invalidTransferTests) + { + for (auto const flag : + {static_cast(lsfMPTLocked), + ~lsfMPTCanTransfer, + ~lsfMPTCanTrade, + 0u}) + { + MPTID id{}; + auto const isSuccess = !enabled || flag == 0 || + (tx == ttPAYMENT && !crossCurrencyPayment && (flag == ~lsfMPTCanTrade)) || + (tx == ttAMM_WITHDRAW && + (flag == ~lsfMPTCanTrade || flag == ~lsfMPTCanTransfer)); + std::pair const error = isSuccess + ? std::make_pair(TER(tesSUCCESS), TER(tesSUCCESS)) + : std::make_pair(TER(tecINVARIANT_FAILED), TER(tefINVARIANT_FAILED)); + doInvariantCheck( + {{isSuccess ? "" : "invalid MPToken transfer between holders"}}, + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto update = [&](AccountID const& a, std::uint64_t v) { + auto sle = ac.view().peek(keylet::mptoken(id, a)); + if (!sle) + return false; + sle->at(sfMPTAmount) = v; + ac.view().update(sle); + return true; + }; + auto issuanceSle = ac.view().peek(keylet::mptIssuance(id)); + if (!issuanceSle) + return false; + auto const flags = issuanceSle->at(sfFlags); + if (flag == lsfMPTLocked) + { + issuanceSle->at(sfFlags) = flags | lsfMPTLocked; + } + else if (flag != 0u) + { + issuanceSle->at(sfFlags) = flags & flag; + } + issuanceSle->at(sfOutstandingAmount) = 200; + ac.view().update(issuanceSle); + return update(a1, 101) && update(a2, 99); + }, + XRPAmount{}, + STTx{ + tx, + [&](STObject& tx) { + if (crossCurrencyPayment) + { + tx.setFieldAmount( + sfSendMax, STAmount(MPTAmount{100}, MPTIssue{id})); + } + }}, + {error.first, error.second}, + [&](Account const& a1, Account const& a2, Env& env) { + Account const gw("gw"); + env.fund(XRP(1'000), gw); + MPTTester const usd( + {.env = env, .issuer = gw, .holders = {a1, a2}, .pay = 100}); + id = usd.issuanceID(); + if (!enabled) + { + env.disableFeature(featureMPTokensV2); + } + return true; + }); + } + } + } + } + + void + testAMM() + { + testcase << "AMM"; + using namespace jtx; + + MPTID mptID{}; + uint256 ammID{}; + AccountID ammAccountID{}; + Account const gw{"gw"}; + Issue lptIssue{}; + PrettyAsset poolAsset{xrpIssue()}; + + auto deleteAMMAccount = [&](ApplyContext& ac, bool) { + auto sle = ac.view().peek(keylet::account(ammAccountID)); + if (!sle) + return false; + ac.view().erase(sle); + return true; + }; + + auto updateLPTokensBalance = [&](ApplyContext& ac, std::int64_t amount) { + auto sle = ac.view().peek(keylet::amm(ammID)); + if (!sle) + return false; + sle->setFieldAmount(sfLPTokenBalance, STAmount{lptIssue, amount}); + ac.view().update(sle); + return true; + }; + auto updateLPTokensBadAmount = [&](ApplyContext& ac, bool) { + return updateLPTokensBalance(ac, -1); + }; + auto updateLPTokensBadBalance = [&](ApplyContext& ac, bool) { + return updateLPTokensBalance(ac, 200'000'000); + }; + auto updateAMM = [&](ApplyContext& ac, bool) { return updateLPTokensBalance(ac, 10); }; + + auto updateAMMPool = [&](ApplyContext& ac, bool isMPT) { + if (isMPT) + { + auto sle = ac.view().peek(keylet::mptoken(mptID, ammAccountID)); + if (!sle) + return false; + sle->setFieldU64(sfMPTAmount, 1); + ac.view().update(sle); + return true; + } + auto sle = ac.view().peek(keylet::account(ammAccountID)); + if (!sle) + return false; + sle->setFieldAmount(sfBalance, XRP(1)); + ac.view().update(sle); + return true; + }; + + auto test = [&](auto const txType, + auto&& update, + bool isMPT, + TER error = tecINVARIANT_FAILED) { + doInvariantCheck( + {{"AMM"}}, + [&](Account const&, Account const&, ApplyContext& ac) { return update(ac, isMPT); }, + XRPAmount{}, + STTx{txType, [&](STObject& tx) {}}, + {tecINVARIANT_FAILED, error}, + [&](Account const&, Account const&, Env& env) { + env.fund(XRP(1'000), gw); + poolAsset = [&]() -> PrettyAsset { + if (isMPT) + { + MPT const mpt = MPTTester({.env = env, .issuer = gw}); + mptID = mpt.issuanceID; + return mpt; + } + return gw["USD"]; + }(); + AMM const amm(env, gw, XRP(100), poolAsset(100)); + ammAccountID = amm.ammAccount(); + ammID = amm.ammID(); + lptIssue = amm.lptIssue(); + return true; + }); + }; + + for (bool const isMPT : {false, true}) + { + auto const error = isMPT ? TER(tecINVARIANT_FAILED) : TER(tefINVARIANT_FAILED); + for (auto txType : {ttAMM_CREATE, ttAMM_DEPOSIT, ttAMM_CLAWBACK, ttAMM_WITHDRAW}) + { + test(txType, deleteAMMAccount, isMPT, tefINVARIANT_FAILED); + test(txType, updateLPTokensBadAmount, isMPT); + test(txType, updateLPTokensBadBalance, isMPT); + } + for (auto txType : {ttAMM_BID, ttAMM_VOTE}) + { + test(txType, updateAMMPool, isMPT, error); + test(txType, updateLPTokensBadAmount, isMPT); + test(txType, updateLPTokensBadBalance, isMPT); + } + for (auto txType : {ttAMM_DELETE, ttCHECK_CASH, ttOFFER_CREATE, ttPAYMENT}) + { + test(txType, updateAMM, isMPT); + } + } } // Test the invariant overwrite fix for both pre- and post-amendment @@ -4600,6 +4783,7 @@ public: testInvariantOverwrite(defaultAmendments()); testInvariantOverwrite(defaultAmendments() - fixCleanup3_1_3); testVaultComputeCoarsestScale(); + testAMM(); } }; diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 74bdc656df..6906c4aba0 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -3498,10 +3499,10 @@ class MPToken_test : public beast::unit_test::Suite auto const [errBuy, errSell] = [&]() -> std::pair { // Global lock if (lockMPTIssue) - return std::make_pair(tecFROZEN, tecFROZEN); + return std::make_pair(tecLOCKED, tecLOCKED); // Local lock if (lockMPToken) - return std::make_pair(tesSUCCESS, error(tecUNFUNDED_OFFER)); + return std::make_pair(error(tecLOCKED), error(tecUNFUNDED_OFFER)); // MPToken doesn't exist if (requireAuth) return std::make_pair(error(tecNO_AUTH), error(tecUNFUNDED_OFFER)); @@ -3564,51 +3565,77 @@ class MPToken_test : public beast::unit_test::Suite env(offer(alice, btc(10), eth(10)), Ter(tecUNFUNDED_OFFER)); } - // MPTLock flag is set and the account is not the issuer of MPT + // MPTLock flag is set: MPT/MPT offer crossing with independent issuers. + // gw2 issues BTC and gw issues ETH so each asset can be frozen independently. + // Passive setup: bob sells BTC (offer(bob, ETH, BTC)); dan sells ETH (offer(dan, BTC, + // ETH)). { + Account const gw2 = Account("gw2"); Account const bob = Account("bob"); Account const dan = Account("dan"); Env env(*this); - env.fund(XRP(1'000), gw, alice, carol, bob, dan); + env.fund(XRP(1'000), gw, gw2, alice, carol, bob, dan); MPTTester btc( {.env = env, - .issuer = gw, - .holders = {alice, carol, bob, dan}, + .issuer = gw2, + .holders = {alice, carol, bob, dan, gw}, .pay = 100, .flags = tfMPTCanLock | kMptDexFlags}); MPTTester eth( {.env = env, .issuer = gw, - .holders = {alice, carol, bob, dan}, + .holders = {alice, carol, bob, dan, gw2}, .pay = 100, .flags = tfMPTCanLock | kMptDexFlags}); + // bob sells BTC (takerGets=BTC); dan sells ETH (takerGets=ETH) env(offer(bob, eth(10), btc(10)), Txflags(tfPassive)); env(offer(dan, btc(10), eth(10)), Txflags(tfPassive)); + env.close(); - auto test = [&](auto const& flag, bool gwOwner = false) { - btc.set({.holder = carol, .flags = flag}); - btc.set({.holder = alice, .flags = flag}); + // --- Individual lock on BTC --- + // alice sells locked BTC (takerGets): balance zeroed, offer unfunded + btc.set({.holder = alice, .flags = tfMPTLock}); + env(offer(alice, eth(1), btc(1)), Ter(tecUNFUNDED_OFFER)); + btc.set({.holder = alice, .flags = tfMPTUnlock}); + // carol buys locked BTC (takerPays): locked MPToken cannot receive + btc.set({.holder = carol, .flags = tfMPTLock}); + env(offer(carol, btc(1), eth(1)), Ter(tecLOCKED)); + btc.set({.holder = carol, .flags = tfMPTUnlock}); + // gw2 is BTC issuer: individual lock on holders does not affect issuer + env(offer(gw2, eth(1), btc(1))); - if (gwOwner) - { - // Succeeds if the account is the issuer - env(offer(gw, eth(1), btc(1))); - env(offer(gw, btc(1), eth(1))); - } - else - { - auto const err = flag == tfMPTLock ? Ter(tecUNFUNDED_OFFER) : Ter(tesSUCCESS); - env(offer(alice, eth(1), btc(1)), err); - // Offer created by not crossed - env(offer(carol, btc(1), eth(1))); - BEAST_EXPECT(expectOffers(env, carol, 1, {{btc(1), eth(1)}})); - } - }; + // --- Individual lock on ETH --- + // alice sells locked ETH (takerGets): balance zeroed, offer unfunded + eth.set({.holder = alice, .flags = tfMPTLock}); + env(offer(alice, btc(1), eth(1)), Ter(tecUNFUNDED_OFFER)); + eth.set({.holder = alice, .flags = tfMPTUnlock}); + // carol buys locked ETH (takerPays): locked MPToken cannot receive + eth.set({.holder = carol, .flags = tfMPTLock}); + env(offer(carol, eth(1), btc(1)), Ter(tecLOCKED)); + eth.set({.holder = carol, .flags = tfMPTUnlock}); + // gw is ETH issuer: individual lock on holders does not affect issuer + env(offer(gw, btc(1), eth(1))); - test(tfMPTLock); - test(tfMPTLock, true); - test(tfMPTUnlock); + // --- Global lock on BTC --- + // All accounts fail regardless of role: global lock is checked in OfferCreate + // before offer crossing, so it applies even to the issuer. + btc.set({.flags = tfMPTLock}); + env(offer(alice, eth(1), btc(1)), Ter(tecLOCKED)); // alice sells BTC + env(offer(alice, btc(1), eth(1)), Ter(tecLOCKED)); // alice buys BTC + env(offer(gw2, eth(1), btc(1)), Ter(tecLOCKED)); // gw2 is BTC issuer, still fails + btc.set({.flags = tfMPTUnlock}); + + // --- Global lock on ETH --- + eth.set({.flags = tfMPTLock}); + env(offer(alice, btc(1), eth(1)), Ter(tecLOCKED)); // alice sells ETH + env(offer(alice, eth(1), btc(1)), Ter(tecLOCKED)); // alice buys ETH + env(offer(gw, btc(1), eth(1)), Ter(tecLOCKED)); // gw is ETH issuer, still fails + eth.set({.flags = tfMPTUnlock}); + + // --- After all locks cleared: normal crossing succeeds --- + env(offer(alice, eth(1), btc(1))); + env(offer(carol, btc(1), eth(1))); } // MPTRequireAuth flag is set and the account is not authorized @@ -3823,6 +3850,7 @@ class MPToken_test : public beast::unit_test::Suite testcase("Cross Asset Payment"); using namespace test::jtx; Account const gw = Account("gw"); + Account const gw2 = Account("gw2"); Account const alice = Account("alice"); Account const carol = Account("carol"); Account const bob = Account("bob"); @@ -3999,6 +4027,7 @@ class MPToken_test : public beast::unit_test::Suite env(pay(ed, gw, btc(10)), Path(~btc), Sendmax(eth(10))); // BTC is transferred from issuer to bob env(pay(gw, ed, eth(10)), Path(~eth), Sendmax(btc(10))); + // // BTC is transferred from ed to bob, ed is not authorized env(pay(ed, gw, eth(10)), Path(~eth), Sendmax(btc(10)), Ter(tecNO_AUTH)); env.close(); @@ -4013,13 +4042,11 @@ class MPToken_test : public beast::unit_test::Suite env(pay(carol, ed, btc(10)), Path(~btc), Sendmax(eth(10)), Ter(tecPATH_PARTIAL)); env(pay(ed, carol, eth(10)), Path(~eth), Sendmax(btc(10)), Ter(tecPATH_PARTIAL)); env(pay(carol, ed, eth(10)), Path(~eth), Sendmax(btc(10)), Ter(tecPATH_PARTIAL)); - // Fail because BTC, which has CanTransfer disabled, is sent to - // bob + // Fail because BTC, which has CanTransfer disabled, is sent to bob env(pay(ed, gw, eth(10)), Path(~eth), Sendmax(btc(10)), Ter(tecPATH_PARTIAL)); env(pay(ed, gw, btc(10)), Path(~btc), Sendmax(eth(10)), Ter(tesSUCCESS)); env(pay(gw, ed, eth(10)), Path(~eth), Sendmax(btc(10)), Ter(tesSUCCESS)); - // Fail because BTC, which has CanTransfer disabled, is sent to - // ed + // Fail because BTC, which has CanTransfer disabled, is sent to ed env(pay(gw, ed, btc(10)), Path(~btc), Sendmax(eth(10)), Ter(tecPATH_PARTIAL)); env.close(); env(offer(gw, eth(100), btc(100)), Txflags(tfPassive)); @@ -4194,7 +4221,17 @@ class MPToken_test : public beast::unit_test::Suite env(pay(gw, carol, usd(1)), Path(~btc, ~eth, ~usd), Sendmax(XRP(1)), - Ter(tecPATH_PARTIAL)); + Txflags(tfPartialPayment | tfNoRippleDirect), + Ter(tecNO_PERMISSION)); + env.close(); + BEAST_EXPECT(expectOffers(env, bob, 3)); + + env(pay(carol, bob, btc(10)), Sendmax(XRP(10)), Ter(tecNO_PERMISSION)); + env(pay(carol, bob, XRP(10)), Sendmax(btc(10)), Ter(tecNO_PERMISSION)); + env(pay(gw, bob, btc(10)), Sendmax(XRP(10)), Ter(tecNO_PERMISSION)); + env(pay(gw, bob, XRP(10)), Sendmax(btc(10)), Ter(tecNO_PERMISSION)); + env(pay(carol, gw, btc(10)), Sendmax(XRP(10)), Ter(tecNO_PERMISSION)); + env(pay(carol, gw, XRP(10)), Sendmax(btc(10)), Ter(tecNO_PERMISSION)); env.close(); BEAST_EXPECT(expectOffers(env, bob, 3)); } @@ -4229,31 +4266,36 @@ class MPToken_test : public beast::unit_test::Suite auto getMPT = [&](Env& env) { MPTTester const btc( {.env = env, - .issuer = gw, - .holders = {alice, carol, bob}, + .issuer = gw2, + .holders = {alice, carol, bob, gw}, .pay = 100, .flags = tfMPTCanLock | kMptDexFlags}); MPTTester const eth( {.env = env, .issuer = gw, - .holders = {alice, carol, bob}, + .holders = {alice, carol, bob, gw2}, .pay = 100, .flags = tfMPTCanLock | kMptDexFlags}); return std::make_pair(btc, eth); }; auto getIOU = [&](Env& env) { - for (auto const& iou : {gw["BTC"], gw["ETH"]}) + for (auto const& a : {alice, carol, bob}) { - for (auto const& a : {alice, carol, bob}) - { - env(fset(a, asfDefaultRipple)); - env.close(); - env(trust(a, iou(200))); - env(pay(gw, a, iou(100))); - env.close(); - } + env(fset(a, asfDefaultRipple)); + env.close(); + env(trust(a, gw["ETH"](200))); + env(pay(gw, a, gw["ETH"](100))); + env(trust(a, gw2["BTC"](200))); + env(pay(gw2, a, gw2["BTC"](100))); + env.close(); } - return std::make_pair(gw["BTC"], gw["ETH"]); + // gw2 needs an ETH trust line to receive ETH when its offers fill + // gw needs BTC to sell BTC + env(trust(gw2, gw["ETH"](200))); + env(trust(gw, gw2["BTC"](200))); + env(pay(gw2, gw, gw2["BTC"](100))); + env.close(); + return std::make_pair(gw2["BTC"], gw["ETH"]); }; auto lock = [&]( Env& env, Account const& account, Token& token, LockType lock) { @@ -4263,12 +4305,12 @@ class MPToken_test : public beast::unit_test::Suite { if (lock == LockType::Global) { - env(fset(gw, asfGlobalFreeze)); + env(fset(token.account, asfGlobalFreeze)); } else { IOU const iou{account, token.currency}; - env(trust(gw, iou(0), tfSetFreeze)); + env(trust(token.account, iou(0), tfSetFreeze)); } } else if constexpr (std::is_same_v) @@ -4285,7 +4327,7 @@ class MPToken_test : public beast::unit_test::Suite }; auto test = [&](auto&& getTokens, TestArg const& arg) { Env env(*this); - env.fund(XRP(1'000), gw, alice, carol, bob); + env.fund(XRP(1'000), gw, gw2, alice, carol, bob); auto [btc, eth] = getTokens(env); @@ -4303,7 +4345,7 @@ class MPToken_test : public beast::unit_test::Suite } if (arg.globalFlagSell != LockType::None) { - lock(env, gw, btc, LockType::Global); + lock(env, gw2, btc, LockType::Global); } else { @@ -4321,34 +4363,58 @@ class MPToken_test : public beast::unit_test::Suite }; // clang-format off std::vector const tests = { - // src, dst, offer's owner are a holder - {.src = alice, .dst = carol, .offerOwner = bob, .srcFlag = LockType::Individual, .err = tecPATH_DRY}, - // dst can receive IOU even if the account is frozen - {.src = alice, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Individual, .err = tecPATH_DRY, .errIOU = tesSUCCESS}, - {.src = alice, .dst = carol, .offerOwner = bob, .globalFlagBuy = LockType::Global, .err = tecPATH_DRY}, - {.src = alice, .dst = carol, .offerOwner = bob, .globalFlagSell = LockType::Global, .err = tecPATH_DRY}, - // offer's owner can receive IOU even if the account is frozen - {.src = alice, .dst = carol, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err = - tecPATH_PARTIAL, .errIOU = tesSUCCESS}, - {.src = alice, .dst = carol, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err = tecPATH_PARTIAL}, - // src, dst are a holder, offer's owner is an issuer - {.src = alice, .dst = carol, .offerOwner = gw, .srcFlag = LockType::Individual, .err = tecPATH_DRY}, - // dst can receive IOU even if the account is frozen - {.src = alice, .dst = carol, .offerOwner = gw, .dstFlag = LockType::Individual, .err = tecPATH_DRY, .errIOU = tesSUCCESS}, - {.src = alice, .dst = carol, .offerOwner = gw, .globalFlagBuy = LockType::Global, .err = tecPATH_DRY}, - {.src = alice, .dst = carol, .offerOwner = gw, .globalFlagSell = LockType::Global, .err = tecPATH_DRY}, - // src is issuer, dst and offer's owner are a holder - // dst can receive IOU even if the account is frozen - {.src = gw, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Individual, .err = tecPATH_DRY, .errIOU = tesSUCCESS}, - // offer's owner can receive IOU from an issuer even if takerBuys is frozen, MPT offer is unfunded in this case - {.src = gw, .dst = carol, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err = tecPATH_PARTIAL, .errIOU = tesSUCCESS}, - {.src = gw, .dst = carol, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err = tecPATH_PARTIAL}, - // dst is issuer, src and offer's owner are a holder - {.src = alice, .dst = gw, .offerOwner = bob, .srcFlag = LockType::Individual, .err = tecPATH_DRY}, - // offer's owner can receive IOU even if the account is frozen - {.src = alice, .dst = gw, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err = tecPATH_PARTIAL, - .errIOU = tesSUCCESS}, - {.src = alice, .dst = gw, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err = tecPATH_PARTIAL}, + // ----- src=alice (holder), dst=carol (holder), offerOwner=bob (holder) ----- + // alice's ETH locked: caught in check() + {.src = alice, .dst = carol, .offerOwner = bob, .srcFlag = LockType::Individual, .err = tecPATH_DRY}, + // carol's BTC locked: caught in MPT check(); IOU dst can still receive when frozen + {.src = alice, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Individual, .err = tecPATH_DRY, .errIOU = tesSUCCESS}, + // ETH globally locked: caught in check() + {.src = alice, .dst = carol, .offerOwner = bob, .globalFlagBuy = LockType::Global, .err = tecPATH_DRY}, + // BTC globally locked: bob's offer unfunded in OfferStream + {.src = alice, .dst = carol, .offerOwner = bob, .globalFlagSell = LockType::Global, .err = tecPATH_PARTIAL}, + // bob's ETH (takerPays) locked: MPT offer unfunded in OfferStream (locked holder cannot receive); IOU can still receive + {.src = alice, .dst = carol, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err = tecPATH_PARTIAL, .errIOU = tesSUCCESS}, + // bob's BTC (takerGets) locked: offer unfunded in OfferStream + {.src = alice, .dst = carol, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err = tecPATH_PARTIAL}, + // ----- src=alice (holder), dst=carol (holder), offerOwner=gw2 (BTC issuer) ----- + // alice's ETH locked: caught in check() + {.src = alice, .dst = carol, .offerOwner = gw2, .srcFlag = LockType::Individual, .err = tecPATH_DRY}, + // carol's BTC locked: caught in MPT check(); IOU dst can still receive when frozen + {.src = alice, .dst = carol, .offerOwner = gw2, .dstFlag = LockType::Individual, .err = tecPATH_DRY, .errIOU = tesSUCCESS}, + // ETH globally locked: caught in check() + {.src = alice, .dst = carol, .offerOwner = gw2, .globalFlagBuy = LockType::Global, .err = tecPATH_DRY}, + // BTC globally locked: gw2 is the BTC issuer, offer always permitted regardless of global freeze + {.src = alice, .dst = carol, .offerOwner = gw2, .globalFlagSell = LockType::Global, .err = tesSUCCESS}, + // ----- src=alice (holder), dst=carol (holder), offerOwner=gw (ETH issuer, BTC holder) ----- + // alice's ETH locked: caught in check() + {.src = alice, .dst = carol, .offerOwner = gw, .srcFlag = LockType::Individual, .err = tecPATH_DRY}, + // carol's BTC locked: caught in MPT check(); IOU dst can still receive when frozen + {.src = alice, .dst = carol, .offerOwner = gw, .dstFlag = LockType::Individual, .err = tecPATH_DRY, .errIOU = tesSUCCESS}, + // ETH globally locked: caught in check() + {.src = alice, .dst = carol, .offerOwner = gw, .globalFlagBuy = LockType::Global, .err = tecPATH_DRY}, + // BTC globally locked: gw holds BTC as a holder (not BTC issuer), offer unfunded in OfferStream + {.src = alice, .dst = carol, .offerOwner = gw, .globalFlagSell = LockType::Global, .err = tecPATH_PARTIAL}, + // ----- src=gw (ETH issuer), dst=carol (holder), offerOwner=bob (holder) ----- + // ETH globally locked, src is ETH issuer: no first MPTEndpointStep so check() passes; + // MPT offer unfunded in OfferStream (globally-locked ETH cannot flow to holder via DEX); IOU issuer can still issue + {.src = gw, .dst = carol, .offerOwner = bob, .srcFlag = LockType::Global, .err = tecPATH_PARTIAL, .errIOU = tesSUCCESS}, + // BTC globally locked: last MPTEndpointStep only checks individual freeze, check() passes; offer unfunded in OfferStream + {.src = gw, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Global, .err = tecPATH_PARTIAL}, + // carol's BTC locked: caught in MPT check(); IOU dst can still receive when frozen + {.src = gw, .dst = carol, .offerOwner = bob, .dstFlag = LockType::Individual, .err = tecPATH_DRY, .errIOU = tesSUCCESS}, + // bob's ETH (takerPays) locked: MPT offer unfunded in OfferStream (locked holder cannot receive); IOU can still receive + {.src = gw, .dst = carol, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err = tecPATH_PARTIAL, .errIOU = tesSUCCESS}, + // bob's BTC (takerGets) locked: offer unfunded in OfferStream + {.src = gw, .dst = carol, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err = tecPATH_PARTIAL}, + // ----- src=alice (holder), dst=gw2 (BTC issuer), offerOwner=bob (holder) ----- + // alice's ETH locked: caught in check() + {.src = alice, .dst = gw2, .offerOwner = bob, .srcFlag = LockType::Individual, .err = tecPATH_DRY}, + // BTC globally locked, dst is BTC issuer: no last MPTEndpointStep so check() passes; offer unfunded in OfferStream + {.src = alice, .dst = gw2, .offerOwner = bob, .dstFlag = LockType::Global, .err = tecPATH_PARTIAL}, + // bob's ETH (takerPays) locked: MPT offer unfunded in OfferStream (locked holder cannot receive); IOU can still receive + {.src = alice, .dst = gw2, .offerOwner = bob, .offerFlagBuy = LockType::Individual, .err = tecPATH_PARTIAL, .errIOU = tesSUCCESS}, + // bob's BTC (takerGets) locked: offer unfunded in OfferStream + {.src = alice, .dst = gw2, .offerOwner = bob, .offerFlagSell = LockType::Individual, .err = tecPATH_PARTIAL}, }; // clang-format on @@ -5949,7 +6015,7 @@ class MPToken_test : public beast::unit_test::Suite env.close(); } - // MPTCanTransfer disabled + // MPTCanTransfer is disabled { Env env{*this, features}; env.fund(XRP(1'000), gw, alice, carol); @@ -5990,49 +6056,50 @@ class MPToken_test : public beast::unit_test::Suite BEAST_EXPECT(env.balance(alice, mpt) == mpt(0)); BEAST_EXPECT(env.balance(gw, mpt) == mpt(0)); - // neither src nor dst is issuer, can still create + // neither src nor dst is issuer, can't create + checkId = keylet::check(alice, env.seq(alice)).key; + env(check::create(alice, carol, mpt(100)), Ter(tecNO_AUTH)); + env.close(); + + // can create now + mpt.set({.account = gw, .mutableFlags = tmfMPTSetCanTransfer}); checkId = keylet::check(alice, env.seq(alice)).key; env(check::create(alice, carol, mpt(100))); env.close(); - - // can't cash - env(check::cash(carol, checkId, mpt(10)), Ter(tecPATH_PARTIAL)); - env.close(); - - // can cash now - mpt.set({.account = gw, .mutableFlags = tmfMPTSetCanTransfer}); env(pay(gw, alice, mpt(10))); env.close(); + // can't cash + mpt.set({.account = gw, .mutableFlags = tmfMPTClearCanTransfer}); + env.close(); + env(check::cash(carol, checkId, mpt(10)), Ter(tecNO_AUTH)); + env.close(); + // can cash + mpt.set({.account = gw, .mutableFlags = tmfMPTSetCanTransfer}); env(check::cash(carol, checkId, mpt(10))); env.close(); } - // MPTCanTrade disabled + // MPTCanTrade is disabled { Env env{*this, features}; env.fund(XRP(1'000), gw, alice, carol); env.close(); - MPTTester mpt( + MPT const mpt = MPTTester( {.env = env, .issuer = gw, .holders = {alice, carol}, - .flags = tfMPTCanTransfer, - .mutableFlags = tmfMPTCanMutateCanTrade}); + .pay = 10, + .flags = tfMPTCanTransfer}); - uint256 checkId{keylet::check(gw, env.seq(gw)).key}; + uint256 const checkId{keylet::check(alice, env.seq(alice)).key}; - // can't create - env(check::create(gw, alice, mpt(100)), Ter(tecNO_PERMISSION)); + // can create + env(check::create(alice, carol, mpt(100))); env.close(); - mpt.set({.account = gw, .mutableFlags = tmfMPTSetCanTrade}); - // can't cash - checkId = keylet::check(gw, env.seq(gw)).key; - env(check::create(gw, carol, mpt(100))); - env.close(); - mpt.set({.account = gw, .mutableFlags = tmfMPTClearCanTrade}); - env(check::cash(carol, checkId, mpt(10)), Ter(tecNO_PERMISSION)); + // can cash + env(check::cash(carol, checkId, mpt(10))); env.close(); } @@ -6087,33 +6154,6 @@ class MPToken_test : public beast::unit_test::Suite env.close(); } - // MPTCanTransfer is not set and the account is not the issuer of MPT - { - Env env{*this, features}; - env.fund(XRP(1'000), gw, alice, carol); - - auto eur = MPTTester( - {.env = env, .issuer = gw, .holders = {alice, carol}, .flags = tfMPTCanTrade}); - uint256 const chkId{getCheckIndex(alice, env.seq(alice))}; - // alice can create - env(check::create(alice, carol, eur(1))); - env.close(); - - // carol can't cash - env(check::cash(carol, chkId, eur(1)), Ter(tecPATH_PARTIAL)); - env.close(); - - // if issuer creates a check then carol can cash since - // it's a transfer from the issuer - uint256 const chkId1{getCheckIndex(gw, env.seq(gw))}; - // alice can't create since CanTransfer is not set - env(check::create(gw, carol, eur(1))); - env.close(); - - env(check::cash(carol, chkId1, eur(1))); - env.close(); - } - // Can create check if src/dst don't own MPT { Env env{*this, features}; @@ -6274,8 +6314,7 @@ class MPToken_test : public beast::unit_test::Suite AMM amm(env, gw, btc(100), usd(100)); env.close(); // alice can't deposit since MPTCanTransfer is not set - amm.deposit( - DepositArg{.account = alice, .tokens = 1'000, .err = Ter(tecNO_PERMISSION)}); + amm.deposit(DepositArg{.account = alice, .tokens = 1'000, .err = Ter(tecNO_AUTH)}); env.close(); // can't clawback since alice is not an LP @@ -6508,8 +6547,8 @@ class MPToken_test : public beast::unit_test::Suite // alice and issuer can't create usd.set({.flags = tfMPTLock}); - createFail(alice, tecFROZEN); - createFail(gw, tecFROZEN); + createFail(alice, tecLOCKED); + createFail(gw, tecLOCKED); // MPTRequireAuth is set @@ -6529,7 +6568,7 @@ class MPToken_test : public beast::unit_test::Suite usd.set({.mutableFlags = tmfMPTClearRequireAuth}); usd.set({.mutableFlags = tmfMPTClearCanTransfer}); // alice can't create - createFail(alice, tecNO_PERMISSION); + createFail(alice, tecNO_AUTH); // issuer can create createDeleteAMM(gw); usd.set({.mutableFlags = tmfMPTSetCanTransfer}); @@ -6575,12 +6614,12 @@ class MPToken_test : public beast::unit_test::Suite {.account = account, .asset1In = usd(1), .asset2In = eur(1), - .err = Ter(tecFROZEN)}); + .err = Ter(tecLOCKED)}); amm.deposit( {.account = account, .asset1In = eur(1), .assets = std::make_pair(eur, usd), - .err = Ter(tecFROZEN)}); + .err = Ter(tecLOCKED)}); } usd.set({.flags = tfMPTUnlock}); @@ -6593,8 +6632,6 @@ class MPToken_test : public beast::unit_test::Suite eur.authorize({.account = carol}); env(pay(gw, carol, eur(1'000'000))); usd.set({.mutableFlags = tmfMPTSetRequireAuth}); - // have to authorize amm account - usd.authorize({.account = gw, .holder = Account{"amm", amm.ammAccount()}}); env.close(); amm.deposit( {.account = carol, .asset1In = usd(1), .asset2In = eur(1), .err = Ter(tecNO_AUTH)}); @@ -6608,6 +6645,16 @@ class MPToken_test : public beast::unit_test::Suite // carol is authorized, can deposit usd.authorize({.account = gw, .holder = carol}); amm.deposit({.account = carol, .tokens = 1'000}); + // Can't authorize or unauthorize AMM pseudo-account + usd.authorize( + {.account = gw, + .holder = Account{"amm", amm.ammAccount()}, + .err = tecNO_PERMISSION}); + usd.authorize( + {.account = gw, + .holder = Account{"amm", amm.ammAccount()}, + .flags = tfMPTUnauthorize, + .err = tecNO_PERMISSION}); // MPTCanTransfer is not set @@ -6615,15 +6662,12 @@ class MPToken_test : public beast::unit_test::Suite usd.set({.mutableFlags = tmfMPTClearCanTransfer}); // carol can't deposit amm.deposit( - {.account = carol, - .asset1In = usd(1), - .asset2In = eur(1), - .err = Ter(tecNO_PERMISSION)}); + {.account = carol, .asset1In = usd(1), .asset2In = eur(1), .err = Ter(tecNO_AUTH)}); amm.deposit( {.account = carol, .asset1In = eur(1), .assets = std::make_pair(eur, usd), - .err = Ter(tecNO_PERMISSION)}); + .err = Ter(tecNO_AUTH)}); // issuer can deposit amm.deposit({.account = gw, .tokens = 1'000}); // carol can deposit @@ -6665,8 +6709,8 @@ class MPToken_test : public beast::unit_test::Suite {.account = account, .asset1Out = usd(1), .asset2Out = eur(1), - .err = Ter(tecFROZEN)}); - amm.withdraw({.account = account, .tokens = 1'000, .err = Ter(tecFROZEN)}); + .err = Ter(tecLOCKED)}); + amm.withdraw({.account = account, .tokens = 1'000, .err = Ter(tecLOCKED)}); // can single withdraw another asset amm.withdraw( {.account = account, .asset1Out = eur(1), .assets = std::make_pair(eur, usd)}); @@ -6692,29 +6736,38 @@ class MPToken_test : public beast::unit_test::Suite usd.authorize({.account = gw, .holder = carol}); amm.withdraw({.account = carol, .asset1Out = usd(1), .asset2Out = eur(1)}); - // MPTCanTransfer is set + // MPTCanTransfer is not set, allow to withdraw usd.set({.mutableFlags = tmfMPTClearRequireAuth}); usd.set({.mutableFlags = tmfMPTClearCanTransfer}); - // carol can't withdraw - amm.withdraw( - {.account = carol, - .asset1Out = usd(1), - .asset2Out = eur(1), - .err = Ter(tecNO_PERMISSION)}); + // carol can withdraw + amm.withdraw({.account = carol, .asset1Out = usd(1), .asset2Out = eur(1)}); // can withdraw another asset amm.withdraw( {.account = carol, .asset1Out = eur(1), .assets = std::make_pair(eur, usd)}); // issuer can withdraw amm.withdraw({.account = gw, .asset1Out = usd(1), .asset2Out = eur(1)}); + // Holder can't transfer to another holder + env.fund(XRP(1'000), bob); + usd.authorize({.account = bob}); + env(pay(carol, bob, usd(1)), Ter(tecNO_AUTH)); + usd.authorize({.account = bob, .flags = tfMPTUnauthorize}); + // Can redeem + env(pay(carol, gw, usd(1))); // carol can withdraw usd.set({.mutableFlags = tmfMPTSetCanTransfer}); amm.withdraw({.account = carol, .asset1Out = usd(1), .asset2Out = eur(1)}); usd.set({.mutableFlags = tmfMPTSetCanTransfer}); + + // MPTCanTrade is not set, allow to withdraw + usd.set({.mutableFlags = tmfMPTClearCanTrade}); - amm.withdraw({.account = gw, .tokens = 1'000, .err = Ter(tecNO_PERMISSION)}); - amm.withdraw({.account = carol, .tokens = 1'000, .err = Ter(tecNO_PERMISSION)}); + amm.withdraw({.account = gw, .tokens = 1'000}); + amm.withdraw({.account = carol, .tokens = 1'000}); + // Can't DEX + amm.deposit( + DepositArg{.account = carol, .asset1In = usd(1), .err = Ter(tecNO_PERMISSION)}); usd.set({.mutableFlags = tmfMPTSetCanTrade}); // MPToken created on withdraw @@ -6733,6 +6786,99 @@ class MPToken_test : public beast::unit_test::Suite } } + void + testFixDoubleOwnerCount(FeatureBitset all) + { + testcase("Fix Double adjustOwnerCount in AMMWithdraw"); + + using namespace jtx; + + // Carol deposits XRP into an XRP/MPT pool, then withdraws MPT. + // Carol has no MPToken before the withdrawal. If the bug exists, + // her ownerCount will be inflated by +1 extra. + Account const gw{"gw"}; + Account const alice{"alice"}; + Account const carol{"carol"}; + Env env(*this, all); + env.fund(XRP(30'000), gw, alice, carol); + env.close(); + + // Create MPT with DEX flags. Only alice is a holder initially. + MPT const btc = MPTTester( + {.env = env, .issuer = gw, .holders = {alice}, .pay = 20'000, .flags = kMptDexFlags}); + + // Alice creates XRP/MPT AMM pool + AMM amm(env, alice, XRP(10'000), btc(10'000)); + + // Carol deposits XRP (single asset) into the pool. + // Carol gets LP tokens but does NOT have an MPToken yet. + auto const carolOwnersBefore = ownerCount(env, carol); + amm.deposit(carol, XRP(1'000), std::nullopt, std::nullopt, tfSingleAsset); + auto const carolOwnersAfterDeposit = ownerCount(env, carol); + // Carol should have +1 for LP token trustline + BEAST_EXPECT(carolOwnersAfterDeposit == carolOwnersBefore + 1); + + auto const carolOwnersBeforeWithdraw = ownerCount(env, carol); + // Carol withdraws single MPT asset. She doesn't have an MPToken, + // so one must be created. Bug: ownerCount incremented twice. + amm.withdraw({.account = carol, .asset1Out = btc(100), .flags = tfSingleAsset}); + auto const carolOwnersAfterWithdraw = ownerCount(env, carol); + + // Expected: +1 for the new MPToken (so total increase = 1) + BEAST_EXPECT(carolOwnersAfterWithdraw == carolOwnersBeforeWithdraw + 1); + } + + void + testTradeAndTransfer() + { + using namespace jtx; + testcase("Trade and Transfer"); + + // Verify canMPTTradeAndTransfer validates the flags when from == to and from != to + + Account const gw{"gw"}; + Account const alice{"alice"}; + Account const carol{"carol"}; + Env env(*this); + env.fund(XRP(1'000), gw, alice, carol); + + MPTTester mpt( + {.env = env, + .issuer = gw, + .holders = {alice, carol}, + .pay = 100, + .flags = kMptDexFlags, + .mutableFlags = tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanTrade}); + + // Both flags are enabled + BEAST_EXPECT(isTesSuccess(canMPTTradeAndTransfer(*env.current(), mpt, gw, gw))); + BEAST_EXPECT(isTesSuccess(canMPTTradeAndTransfer(*env.current(), mpt, gw, alice))); + BEAST_EXPECT(isTesSuccess(canMPTTradeAndTransfer(*env.current(), mpt, alice, alice))); + BEAST_EXPECT(isTesSuccess(canMPTTradeAndTransfer(*env.current(), mpt, alice, carol))); + + // MPTCanTrade is disabled + mpt.set({.mutableFlags = tmfMPTClearCanTrade}); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, gw, gw) == tecNO_PERMISSION); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, gw, alice) == tecNO_PERMISSION); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, alice, alice) == tecNO_PERMISSION); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, alice, carol) == tecNO_PERMISSION); + + // MPTCanTransfer is disabled + mpt.set({.mutableFlags = tmfMPTSetCanTrade}); + mpt.set({.mutableFlags = tmfMPTClearCanTransfer}); + BEAST_EXPECT(isTesSuccess(canMPTTradeAndTransfer(*env.current(), mpt, gw, gw))); + BEAST_EXPECT(isTesSuccess(canMPTTradeAndTransfer(*env.current(), mpt, gw, alice))); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, alice, alice) == tecNO_AUTH); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, alice, carol) == tecNO_AUTH); + + // Both flags are disabled + mpt.set({.mutableFlags = tmfMPTClearCanTrade}); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, gw, gw) == tecNO_PERMISSION); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, gw, alice) == tecNO_PERMISSION); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, alice, alice) == tecNO_PERMISSION); + BEAST_EXPECT(canMPTTradeAndTransfer(*env.current(), mpt, alice, carol) == tecNO_PERMISSION); + } + public: void run() override @@ -6839,6 +6985,12 @@ public: // Test AMM testBasicAMM(all); + + // Test Trade/Transfer + testTradeAndTransfer(); + + // Fixes + testFixDoubleOwnerCount(all); } }; diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index a52ba7fd58..3f88e57bc7 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -2764,7 +2764,7 @@ public: // USD(125) was removed from his account due to the gateway fee. // // A comparable payment would look like this: - // env (pay (bob, alice, USD(100)), sendmax(USD(125))) + // env (pay (bob, alice, USD(100)), Sendmax(USD(125))) env(offer(bob, XRP(1), usd(10'000))); env.close(); @@ -3012,6 +3012,92 @@ public: } }; testHelper2TokensMix(test); + + // Payment trIn: MPT transfer fee must be charged when the payment + // destination is the MPT issuer and MPT crosses the DEX (1-hop). + // Bug: rate() returned parity because strandDst_ == MPT issuer. + // Fix: parity only when this asset IS the final delivered asset. + { + auto const gw = Account("gw_tr1"); + auto const alice = Account("alice_tr1"); + auto const bob = Account("bob_tr1"); + + Env env{*this, features}; + env.fund(XRP(10'000), gw, alice, bob); + env.close(); + + MPT const usd = MPTTester( + {.env = env, .issuer = gw, .holders = {alice, bob}, .transferFee = 25'000}); + + // alice needs MPT(1250): MPT(1000) to bob's offer + MPT(250) transfer fee (25%) + env(pay(gw, alice, usd(1'250))); + // bob's offer: give XRP(1000), want MPT(1000) + env(offer(bob, usd(1'000), XRP(1'000))); + env.close(); + + // alice pays gw (MPT issuer) XRP(1000) using MPT as source + // strand: alice -> [MPT/XRP BookStep] -> gw + // strandDst_ = gw = MPT issuer, strandDeliver_ = XRP + // trIn = rate(MPT, gw): fix charges 25% (MPT != strandDeliver_) + env(pay(alice, gw, XRP(1'000)), Path(~XRP), Sendmax(usd(1'250))); + env.close(); + + // alice consumed all MPT(1250): MPT(1000) to bob + MPT(250) fee + BEAST_EXPECT(env.balance(alice, usd) == usd(0)); + // bob received MPT(1000) net + BEAST_EXPECT(env.balance(bob, usd) == usd(1'000)); + } + + // Payment trIn (2-hop): MPT transfer fee must be charged when MPT is + // intermediate and the destination is the MPT issuer. + // BookStep2(MPT/XRP) prevStep=BookStep1 returns redeems direction + // (ownerPaysTransferFee_=false for Payment), so trIn applies. + // Bug: parity because strandDst_ == MPT issuer. + // Fix: 25% fee because MPT != strandDeliver_(XRP). + { + auto const gw = Account("gw_tr2"); + auto const gw2 = Account("gw2_tr2"); + auto const alice = Account("alice_tr2"); + auto const bob = Account("bob_tr2"); + auto const carol = Account("carol_tr2"); + + Env env{*this, features}; + env.fund(XRP(10'000), gw, gw2, alice, bob, carol); + env.close(); + + MPT const musd = MPTTester( + {.env = env, .issuer = gw, .holders = {bob, carol}, .transferFee = 25'000}); + auto const gusd = gw2["USD"]; + + env(trust(alice, gusd(10'000))); + env(trust(bob, gusd(10'000))); + env.close(); + + env(pay(gw2, alice, gusd(1'000))); + env(pay(gw, bob, musd(1'000))); + env.close(); + + // bob's offer: give MPT(1000), want GUSD(1000) + env(offer(bob, gusd(1'000), musd(1'000))); + // carol's offer: give XRP(800), want MPT(800) + env(offer(carol, musd(800), XRP(800))); + env.close(); + + // Payment: alice GUSD -> [BookStep1: GUSD/MUSD] -> [BookStep2: MUSD/XRP] -> gw XRP + // strandDst_ = gw = MPT issuer, strandDeliver_ = XRP + // BookStep2 trIn: fix = 1.25 -> upstream needs MUSD(1000) for carol's MUSD(800) offer + // => alice must provide full GUSD(1000) to bob's offer; without fix alice only pays + // GUSD(800) + env(pay(alice, gw, XRP(800)), Path(~musd), Sendmax(gusd(1'000))); + env.close(); + + // alice spent all GUSD(1000); bug would leave GUSD(200) unspent + BEAST_EXPECT(env.balance(alice, gusd) == gusd(0)); + // bob gave MPT(1000) and received GUSD(1000) + BEAST_EXPECT(env.balance(bob, musd) == musd(0)); + // carol received MPT(800) net (MPT(200) went to gw as fee) + BEAST_EXPECT(env.balance(carol, musd) == musd(800)); + } } void @@ -4707,6 +4793,101 @@ public: } } + void + testAutoCreateReserve(FeatureBitset features) + { + // When an offer on the book is partially crossed, the payment engine + // auto-creates a new ledger object (MPToken or IOU trustline) for the + // offer owner to hold the incoming asset. This happens inside + // BookStep::forEachOffer (MPT: checkCreateMPT) and BookStep::consumeOffer + // (IOU: directSendNoFeeIOU -> trustCreate) without a reserve sufficiency + // check. The offer owner can therefore end up with more objects than + // their XRP balance can reserve for, consistent with IOU behavior. + + testcase("Auto-Create Object Without Reserve Check During Partial Crossing"); + + using namespace jtx; + + auto const gw = Account{"gateway"}; + auto const alice = Account{"alice"}; + auto const carol = Account{"carol"}; + auto const bob = Account{"bob"}; + + auto test = [&](auto&& getToken, auto&& execTx) { + // MPT/IOU: carol's existing offer buys MPT/IOU by selling XRP. + // carol has no MPToken/Trustline for this issuance. When alice partially crosses + // carol's offer, an MPToken/Trustline is auto-created for carol without checking + // that she can afford the extra reserve slot. + Env env{*this, features}; + + auto const f = env.current()->fees().base; + auto const r = reserve(env, 0); + auto const inc = reserve(env, 1) - r; + + env.fund(XRP(10'000), gw, alice, bob); + + // getToken: + // - Create MPT with CanTransfer + CanTrade; authorize alice as holder. + // - Create IOU trustline + auto const token = getToken(env); + + // carol: reserve(0) + 1 increment + fee covers placing one offer. + // After the offer tx she has exactly reserve(1) + XRP(30). + // XRP(30) < inc (50 XRP), so receiving a second object will put her + // below reserve(2). + if (BEAST_EXPECT(inc > XRP(30))) + env.fund(r + inc + f + XRP(30), carol); + + // carol's offer goes on the book (no counterpart yet). + // TakerPays=Token(30): carol will receive Token when crossed. + // TakerGets=XRP(30): carol will give XRP when crossed. + env(offer(carol, token(30), XRP(30))); + env.require(Owners(carol, 1)); + + // Execute offer create or cross-currency payment + // alice partially crosses carol's offer. + // alice sends Token(15) to carol and receives XRP(15). + // Token: + // - MPT: checkCreateMPT auto-creates an MPToken for carol (no reserve check). + // - IOU: directSendNoFeeIOU auto-creates an Trustline for carol (no reserve check). + execTx(env, token); + + // Carol now owns 2 objects (remaining offer + new MPToken) even + // though her XRP balance is only reserve(1) + XRP(15), which is + // below reserve(2) = reserve(1) + inc. + auto const carolBalance = r + inc + XRP(15); + env.require(Owners(carol, 2), Balance(carol, token(15)), Balance(carol, carolBalance)); + BEAST_EXPECT(carolBalance < r + 2 * inc); // below reserve(2) + }; + std::function const getIOU = [&](Env& env) -> PrettyAsset { + env.trust(gw["USD"](1'000), alice); + env(pay(gw, alice, gw["USD"](100))); + return gw["USD"]; + }; + std::function const getMPT = [&](Env& env) -> PrettyAsset { + MPT const mpT1 = MPTTester({.env = env, .issuer = gw, .holders = {alice}, .pay = 100}); + return mpT1; + }; + for (auto&& getToken : {getIOU, getMPT}) + { + test(getToken, [&](Env& env, PrettyAsset const& token) { + // alice partially crosses carol's offer. + // alice sends Token(15) to carol and receives XRP(15). + // Token is MPT: checkCreateMPT auto-creates an MPToken for carol (no reserve + // check). Token is IOU: directSendNoFeeIOU auto-creates a trustline for carol (no + // reserve check). + env(offer(alice, XRP(15), token(15))); + }); + test(getToken, [&](Env& env, PrettyAsset const& token) { + // Similar to above but with cross-currency payment. + env(pay(alice, bob, XRP(15)), + Sendmax(token(15)), + Path(~XRP), + Txflags(tfNoRippleDirect | tfPartialPayment)); + }); + } + } + void testAll(FeatureBitset features) { @@ -4763,6 +4944,7 @@ public: testRmSmallIncreasedQOffersMPT(features); testFillOrKill(features); testTickSize(features); + testAutoCreateReserve(features); } void diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index af6f4841f4..c6a9a54a53 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -10,9 +10,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -2377,6 +2379,83 @@ class Vault_test : public beast::unit_test::Suite env.close(); } + { + testcase("MPT locked: vault shares inherit underlying lock"); + + Env env{*this, testableAmendments()}; + Account const issuer{"issuer"}; + Account const owner{"owner"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const carol{"carol"}; + env.fund(XRP(10'000), issuer, owner, alice, bob, carol); + env.close(); + Vault const vault{env}; + + MPTTester asset{ + {.env = env, + .issuer = issuer, + .holders = {owner, alice, bob, carol}, + .flags = tfMPTCanTransfer | tfMPTCanTrade | tfMPTCanLock}}; + env(pay(issuer, alice, asset(1'000))); + env(pay(issuer, bob, asset(1'000))); + env.close(); + + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + env(vault.deposit({.depositor = alice, .id = keylet.key, .amount = asset(500)})); + // Bob also deposits so he has a share MPToken to receive into. + env(vault.deposit({.depositor = bob, .id = keylet.key, .amount = asset(500)})); + env.close(); + + auto const shares = [&]() -> PrettyAsset { + auto const sle = env.le(keylet); + BEAST_EXPECT(sle != nullptr); + return MPTIssue(sle->at(sfShareMPTID)); + }(); + auto const shareMptID = shares.raw().get().getMptID(); + auto const shareBalance = [&](Account const& account) { + auto const sle = env.le(keylet::mptoken(shareMptID, account)); + return sle ? sle->at(sfMPTAmount) : 0; + }; + + // Sanity: before the underlying lock, peer-to-peer share + // transfers are allowed. + env(pay(alice, bob, shares(1))); + env.close(); + + // Create the offer while shares are spendable, then lock the + // underlying to test whether a stale offer can still be crossed. + env(offer(alice, XRP(1), shares(1))); + env.close(); + + // Lock the underlying after the vault and share balances exist. + asset.set({.account = issuer, .flags = tfMPTLock}); + env.close(); + + // Direct vault share payment inherits the underlying lock via + // sfReferenceHolding. + BEAST_EXPECT(shareBalance(alice) == 499); + BEAST_EXPECT(shareBalance(bob) == 501); + env(pay(alice, bob, shares(1)), Ter{tecLOCKED}); + env.close(); + BEAST_EXPECT(shareBalance(alice) == 499); + BEAST_EXPECT(shareBalance(bob) == 501); + + // The same inherited lock must also block DEX payment paths that + // would consume an offer selling vault shares. + env(pay(carol, bob, shares(1)), + Sendmax(XRP(1)), + Path(BookSpec{shares.raw()}), + Ter{tecPATH_PARTIAL}); + env.close(); + BEAST_EXPECT(shareBalance(alice) == 499); + BEAST_EXPECT(shareBalance(bob) == 501); + BEAST_EXPECT(expectOffers(env, alice, 1)); + } + { testcase("MPT non-transferable: pre-fixCleanup3_2_0 share transfer succeeds"); @@ -2981,7 +3060,7 @@ class Vault_test : public beast::unit_test::Suite env(tx); env.close(); } - // Behavioural shift introduced by share inheritance: + // Behavioral shift introduced by share inheritance: // before fixCleanup3_2_0 this share Payment succeeded // and the underlying IOU's NoRipple restriction surfaced // only later on Charlie's withdrawal (terNO_RIPPLE). From a37afe13ff33402f72e476d304e8d5f18392611f Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Fri, 22 May 2026 04:30:37 -0700 Subject: [PATCH 16/87] ci: Re-enable full nproc for Linux (#7315) --- .github/workflows/reusable-build-test-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 4c7c41fd0c..cc927512ea 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -181,7 +181,7 @@ jobs: - name: Build the binary working-directory: ${{ env.BUILD_DIR }} env: - BUILD_NPROC: ${{ runner.os == 'Linux' && '16' || steps.nproc.outputs.nproc }} + BUILD_NPROC: ${{ steps.nproc.outputs.nproc }} BUILD_TYPE: ${{ inputs.build_type }} CMAKE_TARGET: ${{ inputs.cmake_target }} run: | From 15dd653e4b2cf3c0165111784d8136b91f0ef91d Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Fri, 22 May 2026 04:30:45 -0700 Subject: [PATCH 17/87] fix: Fix RPM prerelease ordering and start xrpld on DEB install (#7313) --- package/build_pkg.sh | 6 +++++- package/debian/rules | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/package/build_pkg.sh b/package/build_pkg.sh index c834951493..adac2dc169 100755 --- a/package/build_pkg.sh +++ b/package/build_pkg.sh @@ -135,8 +135,12 @@ build_rpm() { # RPM Version can't contain '-'. A pre-release goes in Release with a # leading "0." so 3.2.0-b1 sorts before the final 3.2.0-. + # The order is "0.." (e.g. 0.1.b6) — the Fedora/EPEL + # convention. Reversing to "0.." (e.g. 0.b6.1) breaks + # rpmvercmp against the former because numeric segments outrank alphabetic + # ones, so "0.1.b5" would sort newer than "0.b6.1". local rpm_release="${PKG_RELEASE}" - [[ -n "${VER_SUFFIX}" ]] && rpm_release="0.${VER_SUFFIX}.${PKG_RELEASE}" + [[ -n "${VER_SUFFIX}" ]] && rpm_release="0.${PKG_RELEASE}.${VER_SUFFIX}" set -x rpmbuild -bb \ diff --git a/package/debian/rules b/package/debian/rules index 0fae101358..cd94da7e5b 100644 --- a/package/debian/rules +++ b/package/debian/rules @@ -9,7 +9,7 @@ override_dh_auto_configure override_dh_auto_build override_dh_auto_test: @: override_dh_installsystemd: - dh_installsystemd --no-start xrpld.service + dh_installsystemd --no-stop-on-upgrade xrpld.service dh_installsystemd --name=update-xrpld --no-start update-xrpld.service update-xrpld.timer execute_before_dh_installtmpfiles: From 179e73594ae3d22afaddb144be7905549e276ce5 Mon Sep 17 00:00:00 2001 From: Jingchen Date: Fri, 22 May 2026 12:58:48 +0100 Subject: [PATCH 18/87] fix: Check if the MPT first loss cover can be sent to the broker before deleting the broker (#7125) Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com> --- .../transactors/lending/LoanBrokerDelete.cpp | 14 ++ src/test/app/LoanBroker_test.cpp | 210 ++++++++++++++++++ 2 files changed, 224 insertions(+) diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 805a4612f2..6b77914370 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,19 @@ LoanBrokerDelete::preclaim(PreclaimContext const& ctx) } } + if (ctx.view.rules().enabled(fixCleanup3_2_0)) + { + if (coverAvailable > beast::kZero) + { + auto const brokerPseudo = sleBroker->at(sfAccount); + if (auto const ret = checkFrozen(ctx.view, brokerPseudo, asset)) + { + JLOG(ctx.j.warn()) << "Broker pseudo-account is frozen/locked."; + return ret; + } + } + } + return tesSUCCESS; } diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index c899778391..92949256fd 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -1577,6 +1577,210 @@ class LoanBroker_test : public beast::unit_test::Suite env(loanBroker::set(lender, vaultKeylet.key), Ter(tecFROZEN)); } + void + testLoanBrokerDeleteLockedMPT(FeatureBitset features) + { + testcase << "LoanBrokerDelete - locked broker pseudo-account MPT"; + using namespace jtx; + using namespace loanBroker; + + Account const issuer("issuer"); + Account const alice("alice"); + + auto const withFix = features[fixCleanup3_2_0]; + Env env(*this, features); + env.fund(XRP(100'000), issuer, alice); + env.close(); + + // Create MPT with locking enabled + MPTTester mptt{env, issuer, kMptInitNoFund}; + mptt.create({.flags = tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock}); + + PrettyAsset const mpt{mptt.issuanceID()}; + + // Fund alice + mptt.authorize({.account = alice}); + env.close(); + env(pay(issuer, alice, mpt(100'000))); + env.close(); + + // Create vault + Vault const vault{env}; + auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = mpt}); + env(tx); + env.close(); + + // Deposit into vault + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = mpt(10'000)})); + env.close(); + + // Create loan broker + auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice)); + env(set(alice, vaultKeylet.key)); + env.close(); + + // Deposit cover + env(coverDeposit(alice, brokerKeylet.key, mpt(5'000).value())); + env.close(); + + // Verify cover is deposited + auto const broker = env.le(brokerKeylet); + if (!BEAST_EXPECT(broker)) + return; + BEAST_EXPECT(broker->at(sfCoverAvailable) > 0); + + // Get the broker pseudo-account ID + auto const brokerPseudoID = broker->at(sfAccount); + + // Verify the broker pseudo-account has an MPToken + auto const pseudoMptKey = keylet::mptoken(mptt.issuanceID(), brokerPseudoID); + auto const pseudoMpt = env.le(pseudoMptKey); + if (!BEAST_EXPECT(pseudoMpt)) + return; + + // Issuer locks the broker pseudo-account's individual MPToken + { + json::Value jv; + jv[jss::Account] = issuer.human(); + jv[sfMPTokenIssuanceID] = to_string(mptt.issuanceID()); + jv[jss::Holder] = toBase58(brokerPseudoID); + jv[jss::TransactionType] = jss::MPTokenIssuanceSet; + jv[jss::Flags] = tfMPTLock; + env(jv); + env.close(); + } + + // Verify the pseudo-account's MPToken is now locked + { + auto const sle = env.le(pseudoMptKey); + if (!BEAST_EXPECT(sle)) + return; + BEAST_EXPECT(sle->isFlag(lsfMPTLocked)); + } + + // Record alice's balance before deletion + auto const aliceBalanceBefore = env.balance(alice, mpt); + + // With fixCleanup3_2_0, preclaim() checks the broker pseudo-account's + // freeze/lock state via checkFrozen(), so deletion is blocked. + // Without the fix, the check is missing and the locked cover is + // returned to the owner. + if (withFix) + { + env(del(alice, brokerKeylet.key), Ter(tecLOCKED)); + env.close(); + + // Verify the broker is not deleted + BEAST_EXPECT(env.le(brokerKeylet) != nullptr); + + // Verify alice did not receive the cover despite the lock + auto const aliceBalanceAfter = env.balance(alice, mpt); + BEAST_EXPECT(aliceBalanceAfter == aliceBalanceBefore); + + // Verify the locked MPToken was not deleted + BEAST_EXPECT(env.le(pseudoMptKey) != nullptr); + } + else + { + env(del(alice, brokerKeylet.key), Ter(tesSUCCESS)); + env.close(); + + // Verify the broker is deleted + BEAST_EXPECT(env.le(brokerKeylet) == nullptr); + + // Verify alice received the cover despite the lock + auto const aliceBalanceAfter = env.balance(alice, mpt); + BEAST_EXPECT(aliceBalanceAfter > aliceBalanceBefore); + + // Verify the locked MPToken was deleted + BEAST_EXPECT(env.le(pseudoMptKey) == nullptr); + } + } + + void + testLoanBrokerDeleteFrozenIOU(FeatureBitset features) + { + testcase << "LoanBrokerDelete - frozen broker pseudo-account IOU"; + using namespace jtx; + using namespace loanBroker; + + Account const issuer("issuer"); + Account const alice("alice"); + + auto const withFix = features[fixCleanup3_2_0]; + Env env(*this, features); + env.fund(XRP(100'000), issuer, alice); + env.close(); + + auto const iou = issuer["IOU"]; + + // Set up trust lines and fund alice + env(trust(alice, iou(1'000'000))); + env.close(); + env(pay(issuer, alice, iou(100'000))); + env.close(); + + // Create vault + Vault const vault{env}; + auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = iou.asset()}); + env(tx); + env.close(); + + // Deposit into vault + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = iou(10'000)})); + env.close(); + + // Create loan broker + auto const brokerKeylet = keylet::loanbroker(alice.id(), env.seq(alice)); + env(set(alice, vaultKeylet.key)); + env.close(); + + // Deposit cover + env(coverDeposit(alice, brokerKeylet.key, iou(5'000))); + env.close(); + + // Verify cover is deposited + auto const broker = env.le(brokerKeylet); + if (!BEAST_EXPECT(broker)) + return; + BEAST_EXPECT(broker->at(sfCoverAvailable) > 0); + + // Get the broker pseudo-account + auto const brokerPseudoID = broker->at(sfAccount); + auto const brokerPseudo = Account("BrokerPseudo", brokerPseudoID); + + // Issuer freezes the broker pseudo-account's trust line + env(trust(issuer, brokerPseudo["IOU"](0), tfSetFreeze)); + env.close(); + + // Record alice's balance before deletion attempt + auto const aliceBalanceBefore = env.balance(alice, iou); + + // With fixCleanup3_2_0, preclaim() checks the broker + // pseudo-account's freeze state via checkFrozen(), so + // deletion is blocked early with tecFROZEN. + // Without the fix, preclaim() does not check the pseudo-account, + // but the TransfersNotFrozen invariant catches the frozen transfer + // in doApply() and fails with tecINVARIANT_FAILED. + // Either way, the broker survives and alice's balance is unchanged. + if (withFix) + { + env(del(alice, brokerKeylet.key), Ter(tecFROZEN)); + } + else + { + env(del(alice, brokerKeylet.key), Ter(tecINVARIANT_FAILED)); + } + env.close(); + + // Broker still exists + BEAST_EXPECT(env.le(brokerKeylet) != nullptr); + + // Alice's balance unchanged + auto const aliceBalanceAfter = env.balance(alice, iou); + BEAST_EXPECT(aliceBalanceAfter == aliceBalanceBefore); + } + void testRIPD4274IOU() { @@ -2056,6 +2260,12 @@ public: testRIPD4274(); + testLoanBrokerDeleteLockedMPT(all_); + testLoanBrokerDeleteLockedMPT(all_ - fixCleanup3_2_0); + + testLoanBrokerDeleteFrozenIOU(all_); + testLoanBrokerDeleteFrozenIOU(all_ - fixCleanup3_2_0); + // TODO: Write clawback failure tests with an issuer / MPT that doesn't // have the right flags set. } From dfb9b8ed9a0ad04c1da00c9957fe3c7454930835 Mon Sep 17 00:00:00 2001 From: Bart Date: Fri, 22 May 2026 20:32:12 +0100 Subject: [PATCH 19/87] release: Bump version to 3.2.0-b7 (#7316) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 3d4fff9f04..5613a9366e 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -23,7 +23,7 @@ namespace { //------------------------------------------------------------------------------ // clang-format off // NOLINTNEXTLINE(readability-identifier-naming) -char const* const versionString = "3.2.0-b6" +char const* const versionString = "3.2.0-b7" // clang-format on ; From dcd2ff0b5f2019c40c3f38640e751426df97d3c8 Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Sat, 23 May 2026 02:40:26 -0400 Subject: [PATCH 20/87] fix: Fix non-canonical MPT amount (#7117) 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/protocol/STAmount.h | 17 +- include/xrpl/tx/Transactor.h | 12 + include/xrpl/tx/invariants/InvariantCheck.h | 16 + .../xrpl/tx/transactors/escrow/EscrowCreate.h | 3 + src/libxrpl/protocol/STAmount.cpp | 46 + src/libxrpl/tx/Transactor.cpp | 10 + src/libxrpl/tx/invariants/InvariantCheck.cpp | 31 + .../tx/transactors/check/CheckCash.cpp | 5 + .../tx/transactors/escrow/EscrowCreate.cpp | 12 +- src/test/app/Invariants_test.cpp | 57 ++ src/test/app/MPToken_test.cpp | 867 +++++++++++++++++- src/test/app/OfferMPT_test.cpp | 2 +- 12 files changed, 1074 insertions(+), 4 deletions(-) diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index c576c0da31..bf3e25eedb 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -3,11 +3,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -593,12 +595,25 @@ STAmount::value() const noexcept return *this; } -inline bool +[[nodiscard]] inline bool isLegalNet(STAmount const& value) { return !value.native() || (value.mantissa() <= STAmount::kMaxNativeN); } +[[nodiscard]] inline bool +isLegalMPT(STAmount const& value) +{ + return !value.holds() || + (!value.negative() && value.exponent() == 0 && value.mantissa() <= kMaxMpTokenAmount); +} + +/* Check recursively if an object has invalid MPTAmount or XRPAmount in STAmount field. + * Calls isLegalNet() and isLegalMPT(). + */ +[[nodiscard]] bool +hasInvalidAmount(STBase const& field, beast::Journal j); + //------------------------------------------------------------------------------ // // Operators diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index 61d943c4d5..1440a5097f 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -398,6 +398,15 @@ private: 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. + */ + static NotTEC + preflightUniversal(PreflightContext const& ctx); + /** Check transaction-specific invariants only. * * Walks every modified ledger entry via visitInvariantEntry, then @@ -463,6 +472,9 @@ Transactor::invokePreflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx, T::getFlagsMask(ctx))) return ret; + if (auto const ret = preflightUniversal(ctx)) + return ret; + if (auto const ret = T::preflight(ctx)) return ret; diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index 5996039bf0..d4c0154269 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -373,6 +373,21 @@ 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 + * transaction applies. + */ +class ValidAmounts +{ + std::vector> afterEntries_; + +public: + void + visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + + [[nodiscard]] bool + finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; +}; + // additional invariant checks can be declared above and then added to this // tuple using InvariantChecks = std::tuple< @@ -402,6 +417,7 @@ using InvariantChecks = std::tuple< ValidLoan, ValidVault, ValidMPTPayment, + ValidAmounts, ValidMPTTransfer>; /** diff --git a/include/xrpl/tx/transactors/escrow/EscrowCreate.h b/include/xrpl/tx/transactors/escrow/EscrowCreate.h index 8682ed7369..2e9da89896 100644 --- a/include/xrpl/tx/transactors/escrow/EscrowCreate.h +++ b/include/xrpl/tx/transactors/escrow/EscrowCreate.h @@ -16,6 +16,9 @@ public: static TxConsequences makeTxConsequences(PreflightContext const& ctx); + static bool + checkExtraFeatures(PreflightContext const& ctx); + static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index c40beabf12..1ba9cd042f 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -1222,6 +1224,50 @@ operator-(STAmount const& value) STAmount::Unchecked{}); } +static bool +hasInvalidAmount(STBase const& field, int depth, beast::Journal j); + +static bool +hasInvalidAmount(STObject const& object, int depth, beast::Journal j) +{ + return std::ranges::any_of( + object, [&](STBase const& field) { return hasInvalidAmount(field, depth, j); }); +} + +static bool +hasInvalidAmount(STArray const& array, int depth, beast::Journal j) +{ + return std::ranges::any_of( + array, [&](STObject const& object) { return hasInvalidAmount(object, depth, j); }); +} + +static bool +hasInvalidAmount(STBase const& field, int depth, beast::Journal j) +{ + if (depth > 10) + { + JLOG(j.error()) << "hasInvalidAmount: depth exceeds 10"; + return true; + } + + if (auto const amount = dynamic_cast(&field)) + return !isLegalMPT(*amount) || !isLegalNet(*amount); + + if (auto const object = dynamic_cast(&field)) + return hasInvalidAmount(*object, depth + 1, j); + + if (auto const array = dynamic_cast(&field)) + return hasInvalidAmount(*array, depth + 1, j); + + return false; +} + +bool +hasInvalidAmount(STBase const& field, beast::Journal j) +{ + return hasInvalidAmount(field, 0, j); +} + //------------------------------------------------------------------------------ // // Arithmetic diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 97f2cabff2..28fa059902 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include // IWYU pragma: keep @@ -256,6 +257,15 @@ Transactor::preflight2(PreflightContext const& ctx) return tesSUCCESS; } +NotTEC +Transactor::preflightUniversal(PreflightContext const& ctx) +{ + if (ctx.rules.enabled(fixCleanup3_2_0) && hasInvalidAmount(ctx.tx, ctx.j)) + return temBAD_AMOUNT; + + return tesSUCCESS; +} + //------------------------------------------------------------------------------ Transactor::Transactor(ApplyContext& ctx) diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 3e51b6f877..0154dca747 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -1080,4 +1080,35 @@ NoModifiedUnmodifiableFields::finalize( return true; } +void +ValidAmounts::visitEntry( + bool isDelete, + std::shared_ptr const&, + std::shared_ptr const& after) +{ + if (!isDelete && after) + afterEntries_.push_back(after); +} + +bool +ValidAmounts::finalize( + STTx const&, + TER const, + XRPAmount const, + ReadView const& view, + beast::Journal const& j) const +{ + bool const badLedgerEntry = std::ranges::any_of( + afterEntries_, [&](auto const& sle) { return hasInvalidAmount(*sle, j); }); + + if (badLedgerEntry) + { + JLOG(j.fatal()) + << "Invariant failed: ledger entry contains non-canonical MPT or XRP amount"; + return !view.rules().enabled(fixCleanup3_2_0); + } + + return true; +} + } // namespace xrpl diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index cfc133b501..af903ff177 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -139,6 +139,11 @@ CheckCash::preclaim(PreclaimContext const& ctx) }(ctx.tx)}; STAmount const sendMax = sleCheck->at(sfSendMax); + // A legacy Check may contain a non-canonical MPT sfSendMax. Universal + // preflight only validates the CheckCash transaction, not the stored Check. + if (ctx.view.rules().enabled(fixCleanup3_2_0) && !isLegalMPT(sendMax)) + return tefBAD_LEDGER; + if (!equalTokens(value.asset(), sendMax.asset())) { JLOG(ctx.j.warn()) << "Check cash does not match check currency."; diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 0b1db125f6..4de302db3e 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -81,6 +81,16 @@ EscrowCreate::makeTxConsequences(PreflightContext const& ctx) return TxConsequences{ctx.tx, isXRP(amount) ? amount.xrp() : beast::kZero}; } +bool +EscrowCreate::checkExtraFeatures(PreflightContext const& ctx) +{ + // Only require featureMPTokensV1 when the escrow amount is an MPT and + // fixCleanup3_2_0 is active; XRP/IOU escrows are unaffected by this gate. + if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx[sfAmount].holds()) + return ctx.rules.enabled(featureMPTokensV1); + return true; +} + template static NotTEC escrowCreatePreflightHelper(PreflightContext const& ctx); @@ -103,7 +113,7 @@ template <> NotTEC escrowCreatePreflightHelper(PreflightContext const& ctx) { - if (!ctx.rules.enabled(featureMPTokensV1)) + if (!ctx.rules.enabled(fixCleanup3_2_0) && !ctx.rules.enabled(featureMPTokensV1)) return temDISABLED; auto const amount = ctx.tx[sfAmount]; diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 6fd1904992..cc16948553 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -4065,6 +4065,63 @@ class Invariants_test : public beast::unit_test::Suite using namespace test::jtx; testcase << "MPT"; + MPTIssue const nonCanonicalMPTIssue{makeMptID(1, AccountID(0x4985601))}; + auto const nonCanonicalMPTAmount = [&](SField const& field) { + return STAmount{ + field, + nonCanonicalMPTIssue, + kMaxMpTokenAmount + std::uint64_t{1}, + 0, + false, + STAmount::Unchecked{}}; + }; + auto const negativeMPTAmount = [&](SField const& field) { + return STAmount{field, nonCanonicalMPTIssue, 2, 0, true, STAmount::Unchecked{}}; + }; + auto const nonCanonicalMPTPayment = [&]() { + return STTx{ttPAYMENT, [&](STObject& tx) { + tx.setFieldAmount(sfAmount, nonCanonicalMPTAmount(sfAmount)); + }}; + }; + + doInvariantCheck( + Env{*this, defaultAmendments() - fixCleanup3_2_0}, + {}, + [](Account const&, Account const&, ApplyContext&) { return true; }, + XRPAmount{}, + nonCanonicalMPTPayment(), + {tesSUCCESS, tesSUCCESS}); + + doInvariantCheck( + {{"ledger entry contains non-canonical MPT or XRP amount"}}, + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); + if (!sle) + return false; + + auto sleNew = std::make_shared(keylet::check(a1.id(), (*sle)[sfSequence])); + sleNew->setAccountID(sfAccount, a1.id()); + sleNew->setAccountID(sfDestination, a2.id()); + sleNew->setFieldAmount(sfSendMax, nonCanonicalMPTAmount(sfSendMax)); + ac.view().insert(sleNew); + return true; + }); + + doInvariantCheck( + {{"ledger entry contains non-canonical MPT or XRP amount"}}, + [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto const sle = ac.view().peek(keylet::account(a1.id())); + if (!sle) + return false; + + auto sleNew = std::make_shared(keylet::check(a1.id(), (*sle)[sfSequence])); + sleNew->setAccountID(sfAccount, a1.id()); + sleNew->setAccountID(sfDestination, a2.id()); + sleNew->setFieldAmount(sfSendMax, negativeMPTAmount(sfSendMax)); + ac.view().insert(sleNew); + return true; + }); + // MPT OutstandingAmount > MaximumAmount doInvariantCheck( {{"OutstandingAmount overflow"}}, diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 6906c4aba0..3d6cff0885 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -25,11 +25,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -49,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -57,13 +60,17 @@ #include #include +#include #include #include #include +#include +#include #include #include #include #include +#include #include #include #include @@ -2115,6 +2122,864 @@ class MPToken_test : public beast::unit_test::Suite BEAST_EXPECT(txWithAmounts.empty()); } + void + testNonCanonicalMPTAmountCleanup(FeatureBitset features) + { + using namespace test::jtx; + using namespace std::literals; + FeatureBitset const withoutFix = features - fixCleanup3_2_0; + FeatureBitset const withFix = features | fixCleanup3_2_0; + FeatureBitset const withoutFixAndV2 = withoutFix - featureMPTokensV2; + FeatureBitset const withFixAndWithoutV2 = withFix - featureMPTokensV2; + + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const gw{"gw"}; + + using MPTValue = MPTAmount::value_type; + MPTValue const mptMin = std::numeric_limits::min(); + MPTValue const mptMax = std::numeric_limits::max(); + std::uint64_t const u64Max = std::numeric_limits::max(); + std::uint64_t const firstInvalidMPTMantissa = static_cast(mptMax) + 1; + MPTValue const alice0 = 10'000; + MPTValue const gw0 = -20'000; + TER const success = tesSUCCESS; + TER const invariantFailed = tecINVARIANT_FAILED; + TER const pathPartial = tecPATH_PARTIAL; + TER const badAmountTer = temBAD_AMOUNT; + + struct BadMPTAmount + { + std::string_view name; + std::uint64_t mantissa; + bool negative; + MPTValue mptValue; + TER issuerToHolderPreFixTer; + TER holderSourcePreFixTer; + MPTValue issuerToHolderAliceAfterPreFix; + MPTValue issuerToHolderIssuerAfterPreFix; + MPTValue issuerToHolderAliceAfterPostFix; + MPTValue issuerToHolderIssuerAfterPostFix; + }; + // clang-format off + std::array const badMPTAmounts = {{ + { .name="INT64_MAX + 1", .mantissa=firstInvalidMPTMantissa, .negative=false, .mptValue=mptMin, .issuerToHolderPreFixTer=invariantFailed, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0, .issuerToHolderIssuerAfterPreFix=gw0, .issuerToHolderAliceAfterPostFix=alice0 - 1, .issuerToHolderIssuerAfterPostFix=gw0 + 1}, + { .name="INT64_MAX + 10", .mantissa=firstInvalidMPTMantissa + 9, .negative=false, .mptValue=mptMin + 9, .issuerToHolderPreFixTer=invariantFailed, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0, .issuerToHolderIssuerAfterPreFix=gw0, .issuerToHolderAliceAfterPostFix=alice0 - 1, .issuerToHolderIssuerAfterPostFix=gw0 + 1}, + { .name="UINT64_MAX - 9998", .mantissa=u64Max - 9'998, .negative=false, .mptValue=MPTValue{-9'999}, .issuerToHolderPreFixTer=success, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0 - 9'999, .issuerToHolderIssuerAfterPreFix=gw0 + 9'999, .issuerToHolderAliceAfterPostFix=alice0 - 10'000, .issuerToHolderIssuerAfterPostFix=gw0 + 10'000}, + { .name="UINT64_MAX - 9", .mantissa=u64Max - 9, .negative=false, .mptValue=MPTValue{-10}, .issuerToHolderPreFixTer=success, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0 - 10, .issuerToHolderIssuerAfterPreFix=gw0 + 10, .issuerToHolderAliceAfterPostFix=alice0 - 11, .issuerToHolderIssuerAfterPostFix=gw0 + 11}, + { .name="UINT64_MAX - 1", .mantissa=u64Max - 1, .negative=false, .mptValue=MPTValue{-2}, .issuerToHolderPreFixTer=success, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0 - 2, .issuerToHolderIssuerAfterPreFix=gw0 + 2, .issuerToHolderAliceAfterPostFix=alice0 - 3, .issuerToHolderIssuerAfterPostFix=gw0 + 3}, + { .name="UINT64_MAX", .mantissa=u64Max, .negative=false, .mptValue=MPTValue{-1}, .issuerToHolderPreFixTer=success, .holderSourcePreFixTer=pathPartial, .issuerToHolderAliceAfterPreFix=alice0 - 1, .issuerToHolderIssuerAfterPreFix=gw0 + 1, .issuerToHolderAliceAfterPostFix=alice0 - 2, .issuerToHolderIssuerAfterPostFix=gw0 + 2}, + { .name="-2", .mantissa=std::uint64_t{2}, .negative=true, .mptValue=MPTValue{-2}, .issuerToHolderPreFixTer=badAmountTer, .holderSourcePreFixTer=badAmountTer, .issuerToHolderAliceAfterPreFix=alice0, .issuerToHolderIssuerAfterPreFix=gw0, .issuerToHolderAliceAfterPostFix=alice0 - 1, .issuerToHolderIssuerAfterPostFix=gw0 + 1} + }}; + // clang-format on + auto const badMPTAmount = [&](MPTIssue const& issue, BadMPTAmount const& bad) { + return STAmount{issue, bad.mantissa, 0, bad.negative, STAmount::Unchecked{}}; + }; + auto const makeIssue = [&](Env& env) { + MPTTester const mpt{ + {.env = env, + .issuer = gw, + .holders = {alice, bob}, + .pay = 10'000, + .flags = tfMPTCanTransfer | tfMPTCanTrade | tfMPTCanEscrow | tfMPTCanClawback}}; + return MPTIssue{mpt.issuanceID()}; + }; + auto const withNonCanonicalMPTAmount = + [](JTx jt, SField const& field, STAmount const& amount, Account const& signer) { + STTx tx{*jt.stx}; + tx.setFieldAmount(field, amount); + tx.sign(signer.pk(), signer.sk()); + jt.stx = std::make_shared(tx); + return jt; + }; + auto const roundTrip = [](STTx const& tx) { + Serializer s; + tx.add(s); + SerialIter sit{s.slice()}; + return STTx{sit}; + }; + auto const expectRoundTripBadMPT = + [&](JTx const& jt, SField const& field, BadMPTAmount const& bad) { + auto const roundTripped = roundTrip(*jt.stx); + auto const persisted = roundTripped.getFieldAmount(field); + BEAST_EXPECT(persisted.holds()); + BEAST_EXPECT(persisted.mantissa() == bad.mantissa); + BEAST_EXPECT(persisted.exponent() == 0); + BEAST_EXPECT(persisted.negative() == bad.negative); + BEAST_EXPECT(persisted.mpt().value() == bad.mptValue); + if (!bad.negative) + BEAST_EXPECT(persisted.mantissa() > kMaxMpTokenAmount); + }; + + for (auto const& bad : badMPTAmounts) + { + testcase("fixCleanup3_2_0 rejects non-canonical MPT Payment amounts"); + { + Env env{*this, withoutFixAndV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto malformedHolderToHolder = withNonCanonicalMPTAmount( + env.jt(pay(alice, bob, STAmount{issue, std::uint64_t{1}})), + sfAmount, + badAmount, + alice); + expectRoundTripBadMPT(malformedHolderToHolder, sfAmount, bad); + malformedHolderToHolder.ter = bad.holderSourcePreFixTer; + env.submit(malformedHolderToHolder); + env.close(); + BEAST_EXPECT( + (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue})); + + env.enableFeature(fixCleanup3_2_0); + env.close(); + env(env.jt(pay(bob, alice, STAmount{issue, std::uint64_t{1}})), Ter{tesSUCCESS}); + env.close(); + BEAST_EXPECT( + (env.balance(alice, issue).value() == STAmount{MPTAmount{10'001}, issue})); + BEAST_EXPECT( + (env.balance(bob, issue).value() == STAmount{MPTAmount{9'999}, issue})); + BEAST_EXPECT( + (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue})); + } + { + Env env{*this, envconfig(), withoutFixAndV2, nullptr, beast::Severity::Disabled}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto malformedIssuerToHolder = withNonCanonicalMPTAmount( + env.jt(pay(gw, alice, STAmount{issue, std::uint64_t{1}})), + sfAmount, + badAmount, + gw); + expectRoundTripBadMPT(malformedIssuerToHolder, sfAmount, bad); + malformedIssuerToHolder.ter = bad.issuerToHolderPreFixTer; + env.submit(malformedIssuerToHolder); + env.close(); + BEAST_EXPECT( + (env.balance(alice, issue).value() == + STAmount{MPTAmount{bad.issuerToHolderAliceAfterPreFix}, issue})); + BEAST_EXPECT( + (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(gw, issue).value() == + STAmount{MPTAmount{bad.issuerToHolderIssuerAfterPreFix}, issue})); + + env.enableFeature(fixCleanup3_2_0); + env.close(); + env(env.jt(pay(alice, gw, STAmount{issue, std::uint64_t{1}})), Ter{tesSUCCESS}); + env.close(); + BEAST_EXPECT( + (env.balance(alice, issue).value() == + STAmount{MPTAmount{bad.issuerToHolderAliceAfterPostFix}, issue})); + BEAST_EXPECT( + (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(gw, issue).value() == + STAmount{MPTAmount{bad.issuerToHolderIssuerAfterPostFix}, issue})); + } + { + Env env{*this, withoutFixAndV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto malformedHolderToIssuer = withNonCanonicalMPTAmount( + env.jt(pay(alice, gw, STAmount{issue, std::uint64_t{1}})), + sfAmount, + badAmount, + alice); + expectRoundTripBadMPT(malformedHolderToIssuer, sfAmount, bad); + malformedHolderToIssuer.ter = bad.holderSourcePreFixTer; + env.submit(malformedHolderToIssuer); + env.close(); + BEAST_EXPECT( + (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue})); + + env.enableFeature(fixCleanup3_2_0); + env.close(); + env(env.jt(pay(gw, alice, STAmount{issue, std::uint64_t{1}})), Ter{tesSUCCESS}); + env.close(); + BEAST_EXPECT( + (env.balance(alice, issue).value() == STAmount{MPTAmount{10'001}, issue})); + BEAST_EXPECT( + (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'001}, issue})); + } + { + Env env{*this, withFixAndWithoutV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt(pay(alice, bob, STAmount{issue, std::uint64_t{1}})), + sfAmount, + badAmount, + alice); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + { + Env env{*this, withFixAndWithoutV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt(pay(gw, alice, STAmount{issue, std::uint64_t{1}})), + sfAmount, + badAmount, + gw); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + { + Env env{*this, withFixAndWithoutV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt(pay(alice, gw, STAmount{issue, std::uint64_t{1}})), + sfAmount, + badAmount, + alice); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + + testcase("fixCleanup3_2_0 rejects non-canonical MPT Check amounts"); + { + Env env{*this, envconfig(), withoutFix, nullptr, beast::Severity::Disabled}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badSendMax = badMPTAmount(issue, bad); + auto const checkSeq = env.seq(alice); + auto tx = withNonCanonicalMPTAmount( + env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})), + sfSendMax, + badSendMax, + alice); + tx.ter = bad.negative ? TER{temBAD_AMOUNT} : TER{tesSUCCESS}; + env.submit(tx); + env.close(); + + auto const checkKeylet = keylet::check(alice.id(), checkSeq); + auto const sleCheck = env.le(checkKeylet); + BEAST_EXPECT((sleCheck != nullptr) == !bad.negative); + if (sleCheck && !bad.negative) + { + auto const persisted = sleCheck->getFieldAmount(sfSendMax); + BEAST_EXPECT(persisted.holds()); + BEAST_EXPECT(persisted.mantissa() == bad.mantissa); + BEAST_EXPECT(persisted.negative() == bad.negative); + } + } + { + Env env{*this, envconfig(), withoutFix, nullptr, beast::Severity::Disabled}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badSendMax = badMPTAmount(issue, bad); + auto const checkSeq = env.seq(alice); + auto tx = withNonCanonicalMPTAmount( + env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})), + sfSendMax, + badSendMax, + alice); + tx.ter = bad.negative ? TER{temBAD_AMOUNT} : TER{tesSUCCESS}; + env.submit(tx); + env.close(); + + auto const checkKeylet = keylet::check(alice.id(), checkSeq); + BEAST_EXPECT((env.le(checkKeylet) != nullptr) == !bad.negative); + if (!bad.negative) + { + // CheckCancel has no amount fields, but it must be able to + // remove a malformed legacy Check while the fix is disabled. + env(env.jt(check::cancel(alice, checkKeylet.key)), Ter{tesSUCCESS}); + env.close(); + BEAST_EXPECT(env.le(checkKeylet) == nullptr); + } + } + { + Env env{*this, envconfig(), withoutFix, nullptr, beast::Severity::Disabled}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badSendMax = badMPTAmount(issue, bad); + auto const checkSeq = env.seq(alice); + auto tx = withNonCanonicalMPTAmount( + env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})), + sfSendMax, + badSendMax, + alice); + tx.ter = bad.negative ? TER{temBAD_AMOUNT} : TER{tesSUCCESS}; + env.submit(tx); + env.close(); + + auto const checkKeylet = keylet::check(alice.id(), checkSeq); + BEAST_EXPECT((env.le(checkKeylet) != nullptr) == !bad.negative); + if (!bad.negative) + { + env.enableFeature(fixCleanup3_2_0); + env.close(); + + // Once the fix is enabled, CheckCancel should still remove + // a legacy Check because it does not consume the bad amount. + env(env.jt(check::cancel(alice, checkKeylet.key)), Ter{tesSUCCESS}); + env.close(); + BEAST_EXPECT(env.le(checkKeylet) == nullptr); + } + } + { + Env env{*this, envconfig(), withoutFix, nullptr, beast::Severity::Disabled}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badSendMax = badMPTAmount(issue, bad); + auto const checkSeq = env.seq(alice); + auto tx = withNonCanonicalMPTAmount( + env.jt(check::create(alice, bob, STAmount{issue, std::uint64_t{10}})), + sfSendMax, + badSendMax, + alice); + tx.ter = bad.negative ? TER{temBAD_AMOUNT} : TER{tesSUCCESS}; + env.submit(tx); + env.close(); + + auto const checkKeylet = keylet::check(alice.id(), checkSeq); + BEAST_EXPECT((env.le(checkKeylet) != nullptr) == !bad.negative); + if (!bad.negative) + { + env.enableFeature(fixCleanup3_2_0); + env.close(); + + auto const cashAmount = STAmount{sfAmount, issue, std::uint64_t{1}, 0, false}; + env(env.jt(check::cash(bob, checkKeylet.key, cashAmount)), Ter{tefBAD_LEDGER}); + env.close(); + BEAST_EXPECT(env.le(checkKeylet) != nullptr); + } + } + { + Env env{*this, withoutFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const sendMax = STAmount{sfSendMax, issue, std::uint64_t{10}, 0, false}; + auto const checkSeq = env.seq(alice); + env(env.jt(check::create(alice, bob, sendMax)), Ter{tesSUCCESS}); + env.close(); + + auto const badCashAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + check::cash( + bob, + keylet::check(alice.id(), checkSeq).key, + STAmount{issue, std::uint64_t{1}})), + sfAmount, + badCashAmount, + bob); + expectRoundTripBadMPT(tx, sfAmount, bad); + tx.ter = bad.holderSourcePreFixTer; + env.submit(tx); + env.close(); + BEAST_EXPECT(env.le(keylet::check(alice.id(), checkSeq)) != nullptr); + BEAST_EXPECT( + (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue})); + } + { + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const sendMax = STAmount{sfSendMax, issue, std::uint64_t{10}, 0, false}; + auto const checkSeq = env.seq(alice); + env(env.jt(check::create(alice, bob, sendMax)), Ter{tesSUCCESS}); + env.close(); + + auto const badCashAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + check::cash( + bob, + keylet::check(alice.id(), checkSeq).key, + STAmount{issue, std::uint64_t{1}})), + sfAmount, + badCashAmount, + bob); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + + testcase("fixCleanup3_2_0 rejects non-canonical MPT Escrow amounts"); + { + Env env{*this, withoutFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const escrowSeq = env.seq(alice); + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + escrow::create(alice, bob, STAmount{issue, std::uint64_t{1}}), + escrow::kFinishTime(env.now() + 1s)), + sfAmount, + badAmount, + alice); + tx.ter = bad.negative ? TER{temBAD_AMOUNT} : TER{tecINSUFFICIENT_FUNDS}; + env.submit(tx); + env.close(); + BEAST_EXPECT(env.le(keylet::escrow(alice.id(), escrowSeq)) == nullptr); + } + { + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + escrow::create(alice, bob, STAmount{issue, std::uint64_t{1}}), + escrow::kFinishTime(env.now() + 1s)), + sfAmount, + badAmount, + alice); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + + testcase("fixCleanup3_2_0 rejects non-canonical MPT Clawback amounts"); + { + Env env{*this, withoutFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt(claw(gw, STAmount{issue, std::uint64_t{1}}, bob)), + sfAmount, + badAmount, + gw); + expectRoundTripBadMPT(tx, sfAmount, bad); + tx.ter = bad.negative ? TER{temBAD_AMOUNT} : TER{tesSUCCESS}; + env.submit(tx); + env.close(); + + MPTValue const bobAfter = bad.negative ? MPTValue{10'000} : MPTValue{0}; + MPTValue const gwAfter = bad.negative ? MPTValue{-20'000} : MPTValue{-10'000}; + BEAST_EXPECT( + (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(bob, issue).value() == STAmount{MPTAmount{bobAfter}, issue})); + BEAST_EXPECT( + (env.balance(gw, issue).value() == STAmount{MPTAmount{gwAfter}, issue})); + } + { + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt(claw(gw, STAmount{issue, std::uint64_t{1}}, bob)), + sfAmount, + badAmount, + gw); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + env.close(); + + BEAST_EXPECT( + (env.balance(alice, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(bob, issue).value() == STAmount{MPTAmount{10'000}, issue})); + BEAST_EXPECT( + (env.balance(gw, issue).value() == STAmount{MPTAmount{-20'000}, issue})); + } + + testcase("featureMPTokensV2 disabled rejects MPT OfferCreate amounts"); + { + Env env{*this, withoutFixAndV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badTakerPays = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt(offer(alice, STAmount{issue, std::uint64_t{1}}, XRP(10))), + sfTakerPays, + badTakerPays, + alice); + expectRoundTripBadMPT(tx, sfTakerPays, bad); + tx.ter = temDISABLED; + env.submit(tx); + } + { + Env env{*this, withFixAndWithoutV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badTakerPays = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt(offer(alice, STAmount{issue, std::uint64_t{1}}, XRP(10))), + sfTakerPays, + badTakerPays, + alice); + tx.ter = temDISABLED; + env.submit(tx); + } + { + // sfTakerPays is MPT: both amendments active. Negative offers + // fail in OfferCreate::preflight() before the universal check; + // positive non-canonical amounts fail in the universal check. + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badTakerPays = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt(offer(alice, STAmount{issue, std::uint64_t{1}}, XRP(10))), + sfTakerPays, + badTakerPays, + alice); + tx.ter = TER{temBAD_AMOUNT}; + env.submit(tx); + } + { + // sfTakerGets is MPT: both amendments active. Negative offers + // fail in OfferCreate::preflight() before the universal check; + // positive non-canonical amounts fail in the universal check. + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badTakerGets = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt(offer(alice, XRP(10), STAmount{issue, std::uint64_t{1}})), + sfTakerGets, + badTakerGets, + alice); + tx.ter = TER{temBAD_AMOUNT}; + env.submit(tx); + } + + testcase("featureMPTokensV2 disabled rejects MPT AMMCreate amounts"); + { + Env env{*this, withoutFixAndV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + AMM::createJv(alice.id(), STAmount{issue, std::uint64_t{1}}, XRP(1), 0), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + alice); + expectRoundTripBadMPT(tx, sfAmount, bad); + tx.ter = temDISABLED; + env.submit(tx); + } + { + Env env{*this, withFixAndWithoutV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + AMM::createJv(alice.id(), STAmount{issue, std::uint64_t{1}}, XRP(1), 0), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + alice); + tx.ter = temDISABLED; + env.submit(tx); + } + { + // sfAmount is MPT: both amendments active, expect temBAD_AMOUNT + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + AMM::createJv(alice.id(), STAmount{issue, std::uint64_t{1}}, XRP(1), 0), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + alice); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + + testcase("featureMPTokensV2 disabled rejects MPT AMMDeposit amounts"); + { + Env env{*this, withoutFixAndV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + AMM::depositJv( + {.account = alice, + .asset1In = STAmount{issue, std::uint64_t{1}}, + .assets = std::make_pair(Asset{issue}, Asset{xrpIssue()})}), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + alice); + expectRoundTripBadMPT(tx, sfAmount, bad); + tx.ter = temDISABLED; + env.submit(tx); + } + { + Env env{*this, withFixAndWithoutV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + AMM::depositJv( + {.account = alice, + .asset1In = STAmount{issue, std::uint64_t{1}}, + .assets = std::make_pair(Asset{issue}, Asset{xrpIssue()})}), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + alice); + tx.ter = temDISABLED; + env.submit(tx); + } + { + // sfAmount is MPT: both amendments active, expect temBAD_AMOUNT + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + AMM::depositJv( + {.account = alice, + .asset1In = STAmount{issue, std::uint64_t{1}}, + .assets = std::make_pair(Asset{issue}, Asset{xrpIssue()})}), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + alice); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + + testcase("featureMPTokensV2 disabled rejects MPT AMMWithdraw amounts"); + { + Env env{*this, withoutFixAndV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + AMM::withdrawJv( + {.account = alice, + .asset1Out = STAmount{issue, std::uint64_t{1}}, + .assets = std::make_pair(Asset{issue}, Asset{xrpIssue()})}), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + alice); + expectRoundTripBadMPT(tx, sfAmount, bad); + tx.ter = temDISABLED; + env.submit(tx); + } + { + Env env{*this, withFixAndWithoutV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + AMM::withdrawJv( + {.account = alice, + .asset1Out = STAmount{issue, std::uint64_t{1}}, + .assets = std::make_pair(Asset{issue}, Asset{xrpIssue()})}), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + alice); + tx.ter = temDISABLED; + env.submit(tx); + } + { + // sfAmount is MPT: both amendments active, expect temBAD_AMOUNT + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + AMM::withdrawJv( + {.account = alice, + .asset1Out = STAmount{issue, std::uint64_t{1}}, + .assets = std::make_pair(Asset{issue}, Asset{xrpIssue()})}), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + alice); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + + testcase("featureMPTokensV2 disabled rejects MPT AMMClawback amounts"); + { + Env env{*this, withoutFixAndV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + amm::ammClawback( + gw, + alice, + Asset{issue}, + Asset{xrpIssue()}, + std::make_optional(STAmount{issue, std::uint64_t{1}})), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + gw); + expectRoundTripBadMPT(tx, sfAmount, bad); + tx.ter = temDISABLED; + env.submit(tx); + } + { + Env env{*this, withFixAndWithoutV2}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + amm::ammClawback( + gw, + alice, + Asset{issue}, + Asset{xrpIssue()}, + std::make_optional(STAmount{issue, std::uint64_t{1}})), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + gw); + tx.ter = temDISABLED; + env.submit(tx); + } + { + // sfAmount is MPT: both amendments active, expect temBAD_AMOUNT + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + auto tx = withNonCanonicalMPTAmount( + env.jt( + amm::ammClawback( + gw, + alice, + Asset{issue}, + Asset{xrpIssue()}, + std::make_optional(STAmount{issue, std::uint64_t{1}})), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + gw); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + + testcase("fixCleanup3_2_0 rejects non-canonical MPT VaultClawback amounts"); + { + Env env{*this, withFix}; + env.fund(XRP(100'000), alice, bob, gw); + env.close(); + auto const issue = makeIssue(env); + + auto const badAmount = badMPTAmount(issue, bad); + uint256 const fakeVaultId = keylet::vault(gw.id(), 1).key; + auto tx = withNonCanonicalMPTAmount( + env.jt( + Vault::clawback( + {.issuer = gw, + .id = fakeVaultId, + .holder = alice, + .amount = STAmount{issue, std::uint64_t{1}}}), + Fee(static_cast(env.current()->fees().increment.drops()))), + sfAmount, + badAmount, + gw); + tx.ter = temBAD_AMOUNT; + env.submit(tx); + } + } + } + void testTxJsonMetaFields(FeatureBitset features) { @@ -6947,7 +7812,7 @@ public: // Test MPT Amount is invalid in Tx, which don't support MPT testMPTInvalidInTx(all); - + testNonCanonicalMPTAmountCleanup(all); // Test parsed MPTokenIssuanceID in API response metadata testTxJsonMetaFields(all); diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index 3f88e57bc7..e9366f7c32 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -973,7 +973,7 @@ public: // Offers with negative amounts { - env(offer(alice, -usd(1'000), XRP(1'000)), Ter(temBAD_OFFER)); + env(offer(alice, -usd(1'000), XRP(1'000)), Ter(temBAD_AMOUNT)); env.require(Owners(alice, 1), offers(alice, 0)); } From 30de556224bdcdedb1732a8865fcce3b40ab2531 Mon Sep 17 00:00:00 2001 From: Valentin Balaschenko <13349202+vlntb@users.noreply.github.com> Date: Sat, 23 May 2026 15:48:48 +0100 Subject: [PATCH 21/87] fix: Address review feedback on FD/handle guarding (#5823 follow-up) (#7310) --- include/xrpl/server/detail/Door.h | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/include/xrpl/server/detail/Door.h b/include/xrpl/server/detail/Door.h index b72bc9bdea..79d36cae0c 100644 --- a/include/xrpl/server/detail/Door.h +++ b/include/xrpl/server/detail/Door.h @@ -23,7 +23,6 @@ #include #include -#include #endif #include @@ -99,7 +98,10 @@ private: static constexpr std::chrono::milliseconds kMaxAcceptDelay{2000}; std::chrono::milliseconds acceptDelay_{kInitialAcceptDelay}; boost::asio::steady_timer backoffTimer_; - static constexpr double kFreeFdThreshold = 0.70; + static constexpr std::uint64_t kMaxUsedFdPercent = 70; + static constexpr std::chrono::milliseconds kFdSampleInterval{250}; + clock_type::time_point fdSampleAt_; + bool cachedThrottle_{false}; struct FDStats { @@ -280,6 +282,7 @@ Door::Door( , acceptor_(ioContext) , strand_(boost::asio::make_strand(ioContext)) , backoffTimer_(ioContext) + , fdSampleAt_(clock_type::now() - kFdSampleInterval) { reOpen(); } @@ -338,11 +341,11 @@ Door::doAccept(boost::asio::yield_context doYield) { if (shouldThrottleForFds()) { + JLOG(j_.warn()) << "Throttling do_accept for " << acceptDelay_.count() << "ms."; backoffTimer_.expires_after(acceptDelay_); boost::system::error_code tec; backoffTimer_.async_wait(doYield[tec]); acceptDelay_ = std::min(acceptDelay_ * 2, kMaxAcceptDelay); - JLOG(j_.warn()) << "Throttling do_accept for " << acceptDelay_.count() << "ms."; continue; } @@ -359,8 +362,11 @@ Door::doAccept(boost::asio::yield_context doYield) if (ec == boost::asio::error::no_descriptors || ec == boost::asio::error::no_buffer_space) { - JLOG(j_.warn()) << "accept: Too many open files. Pausing for " - << acceptDelay_.count() << "ms."; + char const* const cause = (ec == boost::asio::error::no_descriptors) + ? "too many open files" + : "kernel buffer space exhausted"; + JLOG(j_.warn()) << "accept: " << cause << ". Pausing for " << acceptDelay_.count() + << "ms."; backoffTimer_.expires_after(acceptDelay_); boost::system::error_code tec; @@ -428,14 +434,15 @@ Door::shouldThrottleForFds() #if BOOST_OS_WINDOWS return false; #else - auto const stats = queryFdStats(); - if (!stats || stats->limit == 0) - return false; + auto const now = clock_type::now(); + if (now - fdSampleAt_ < kFdSampleInterval) + return cachedThrottle_; - auto const& s = *stats; - auto const free = (s.limit > s.used) ? (s.limit - s.used) : 0ull; - double const freeRatio = static_cast(free) / static_cast(s.limit); - return freeRatio < kFreeFdThreshold; + fdSampleAt_ = now; + auto const stats = queryFdStats(); + cachedThrottle_ = + stats && stats->limit > 0 && stats->used * 100 > stats->limit * kMaxUsedFdPercent; + return cachedThrottle_; #endif } From e34c2667d7b29cc38d83e8ec7190cd7769ec4e9f Mon Sep 17 00:00:00 2001 From: Peter Chen <34582813+PeterChen13579@users.noreply.github.com> Date: Sun, 24 May 2026 16:37:16 -0400 Subject: [PATCH 22/87] fix: Skip deleted book directories and non-root modifications in `ValidBookDirectory` invariant (#7312) --- .../tx/invariants/DirectoryInvariant.cpp | 18 ++++-- src/test/app/Invariants_test.cpp | 64 +++++++++++++++++++ 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/libxrpl/tx/invariants/DirectoryInvariant.cpp b/src/libxrpl/tx/invariants/DirectoryInvariant.cpp index 2a4fac07d0..1624a19830 100644 --- a/src/libxrpl/tx/invariants/DirectoryInvariant.cpp +++ b/src/libxrpl/tx/invariants/DirectoryInvariant.cpp @@ -40,19 +40,27 @@ badExchangeRate(SLE const& dir) void ValidBookDirectory::visitEntry( - bool, + bool isDelete, std::shared_ptr const& before, std::shared_ptr const& after) { // New root directories must have matching exchange-rate metadata. New - // child directories must point to an existing root. + // child directories, and modified directories that change sfRootIndex, must + // point to an existing root. - // Only validate newly-created directories; LedgerStateFix handles legacy - // bad exchange-rate metadata. - if (badBookDirectory_ || before || !after || after->getType() != ltDIR_NODE) + // Only validate newly-created directories and sfRootIndex changes; + // LedgerStateFix handles legacy bad exchange-rate metadata. Skip deletions + // because `after` is not guaranteed to be null. + if (badBookDirectory_ || isDelete || !after || after->getType() != ltDIR_NODE) return; auto const rootIndex = after->getFieldH256(sfRootIndex); + // Ignore ordinary modifications that do not change which root this + // directory belongs to. That tolerates legacy bad exchange-rate metadata + // during normal operation while still checking sfRootIndex changes. + if (before && before->getFieldH256(sfRootIndex) == rootIndex) + return; + if (after->key() == rootIndex && !badBookDirectory_) { badBookDirectory_ = badBookDirectory_ || badExchangeRate(*after); diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index cc16948553..c8a6e813de 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -2137,6 +2137,70 @@ class Invariants_test : public beast::unit_test::Suite BEAST_EXPECT( invariant.finalize(makeOfferCreateTx(), tesSUCCESS, XRPAmount{}, view, jlog)); } + + // A bad root is rejected when added, ignored when a legacy bad root is + // modified without changing sfRootIndex or deleted, and checked when a + // modified directory changes sfRootIndex. + { + Env env{*this, defaultAmendments()}; + Account const a1{"A1"}; + env.fund(XRP(1000), a1); + env.close(); + + OpenView view{*env.current()}; + auto const directoryQuality = STAmount::kURateOne; + auto const rootDir = getBookRootKey(a1, directoryQuality); + auto const missingRootDir = getBookRootKey(a1, directoryQuality + 1); + auto const badRoot = makeRootPage(rootDir, directoryQuality + 1); + view.rawInsert(badRoot); + + test::StreamSink sink{beast::Severity::Warning}; + beast::Journal const jlog{sink}; + + { + // add + ValidBookDirectory invariant; + invariant.visitEntry(false, nullptr, badRoot); + + BEAST_EXPECT( + !invariant.finalize(makeOfferCreateTx(), tesSUCCESS, XRPAmount{}, view, jlog)); + } + { + // modify (without changing the sfRootIndex) + ValidBookDirectory invariant; + invariant.visitEntry(false, badRoot, badRoot); + + BEAST_EXPECT( + invariant.finalize(makeOfferCreateTx(), tesSUCCESS, XRPAmount{}, view, jlog)); + } + { + // modify (changing sfRootIndex to a missing root) + auto const childBefore = makeChildPage(rootDir); + auto const childAfter = std::make_shared(*childBefore, childBefore->key()); + childAfter->setFieldH256(sfRootIndex, missingRootDir.key); + + ValidBookDirectory invariant; + invariant.visitEntry(false, childBefore, childAfter); + + test::StreamSink missingRootSink{beast::Severity::Warning}; + beast::Journal const missingRootJlog{missingRootSink}; + BEAST_EXPECT(!invariant.finalize( + makeOfferCreateTx(), tesSUCCESS, XRPAmount{}, view, missingRootJlog)); + BEAST_EXPECT( + missingRootSink.messages().str().find("book directory root missing") != + std::string::npos); + } + { + // delete + view.rawErase(badRoot); + BEAST_EXPECT(!view.exists(rootDir)); + + ValidBookDirectory invariant; + invariant.visitEntry(true, badRoot, badRoot); + BEAST_EXPECT( + invariant.finalize(makeOfferCreateTx(), tesSUCCESS, XRPAmount{}, view, jlog)); + } + } } Keylet From a911f9089e7c2f67b564dd888715fbf71e1197fd Mon Sep 17 00:00:00 2001 From: Jingchen Date: Sun, 24 May 2026 21:44:29 +0100 Subject: [PATCH 23/87] fix: Use consistent scale for `debtTotal` (#7093) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- include/xrpl/ledger/helpers/LendingHelpers.h | 15 + .../lending/LoanBrokerCoverClawback.cpp | 29 +- .../lending/LoanBrokerCoverWithdraw.cpp | 6 + .../tx/transactors/lending/LoanPay.cpp | 30 +- .../tx/transactors/lending/LoanSet.cpp | 18 +- src/test/app/Loan_test.cpp | 409 ++++++++++++++++++ 6 files changed, 482 insertions(+), 25 deletions(-) diff --git a/include/xrpl/ledger/helpers/LendingHelpers.h b/include/xrpl/ledger/helpers/LendingHelpers.h index cce41a38c5..b7a1ed6bf2 100644 --- a/include/xrpl/ledger/helpers/LendingHelpers.h +++ b/include/xrpl/ledger/helpers/LendingHelpers.h @@ -203,6 +203,21 @@ getAssetsTotalScale(SLE::const_ref vaultSle) return scale(vaultSle->at(sfAssetsTotal), vaultSle->at(sfAsset)); } +// Compute the minimum required broker cover, rounded consistently. +// DebtTotal is a broker-level aggregate maintained at vault scale, so the +// rounding must also use vault scale — never an individual loan's scale. +inline Number +minimumBrokerCover(Number const& debtTotal, TenthBips32 coverRateMinimum, SLE::const_ref vaultSle) +{ + XRPL_ASSERT( + vaultSle && vaultSle->getType() == ltVAULT, "xrpl::minimumBrokerCover : valid Vault sle"); + NumberRoundModeGuard const mg(Number::RoundingMode::Upward); + return roundToAsset( + vaultSle->at(sfAsset), + tenthBipsOfValue(debtTotal, coverRateMinimum), + getAssetsTotalScale(vaultSle)); +} + TER checkLoanGuards( Asset const& vaultAsset, diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp index 48cb6b90aa..11095fdebe 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -160,15 +161,25 @@ Expected determineClawAmount( SLE const& sleBroker, Asset const& vaultAsset, - std::optional const& amount) + std::optional const& amount, + SLE::const_ref vaultSle, + Rules const& rules) { auto const maxClawAmount = [&]() { - // Always round the minimum required up - NumberRoundModeGuard const mg1(Number::RoundingMode::Upward); - auto const minRequiredCover = - tenthBipsOfValue(sleBroker[sfDebtTotal], TenthBips32(sleBroker[sfCoverRateMinimum])); + auto const minRequiredCover = [&]() { + if (rules.enabled(fixCleanup3_2_0)) + { + return minimumBrokerCover( + sleBroker[sfDebtTotal], TenthBips32(sleBroker[sfCoverRateMinimum]), vaultSle); + } + + // Always round the minimum required up + NumberRoundModeGuard const mg(Number::RoundingMode::Upward); + return tenthBipsOfValue( + sleBroker[sfDebtTotal], TenthBips32(sleBroker[sfCoverRateMinimum])); + }(); // The subtraction probably won't round, but round down if it does. - NumberRoundModeGuard const mg2(Number::RoundingMode::Downward); + NumberRoundModeGuard const mg(Number::RoundingMode::Downward); return sleBroker[sfCoverAvailable] - minRequiredCover; }(); if (maxClawAmount <= beast::kZero) @@ -283,7 +294,8 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx) } } - auto const findClawAmount = determineClawAmount(*sleBroker, vaultAsset, amount); + auto const findClawAmount = + determineClawAmount(*sleBroker, vaultAsset, amount, vault, ctx.view.rules()); if (!findClawAmount) { JLOG(ctx.j.warn()) << "LoanBroker cover is already at minimum."; @@ -345,7 +357,8 @@ LoanBrokerCoverClawback::doApply() auto const vaultAsset = vault->at(sfAsset); - auto const findClawAmount = determineClawAmount(*sleBroker, vaultAsset, amount); + auto const findClawAmount = + determineClawAmount(*sleBroker, vaultAsset, amount, vault, view().rules()); if (!findClawAmount) return tecINTERNAL; // LCOV_EXCL_LINE STAmount const& clawAmount = *findClawAmount; diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp index dbe2de2100..f4c95541f6 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp @@ -142,6 +142,12 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx) // Cover Rate is in 1/10 bips units auto const currentDebtTotal = sleBroker->at(sfDebtTotal); auto const minimumCover = [&]() { + if (ctx.view.rules().enabled(fixCleanup3_2_0)) + { + return minimumBrokerCover( + currentDebtTotal, TenthBips32{sleBroker->at(sfCoverRateMinimum)}, vault); + } + // Always round the minimum required up. // Applies to `tenthBipsOfValue` as well as `roundToAsset`. NumberRoundModeGuard const mg(Number::RoundingMode::Upward); diff --git a/src/libxrpl/tx/transactors/lending/LoanPay.cpp b/src/libxrpl/tx/transactors/lending/LoanPay.cpp index b8b940f3e3..65220573de 100644 --- a/src/libxrpl/tx/transactors/lending/LoanPay.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanPay.cpp @@ -311,6 +311,8 @@ LoanPay::doApply() TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)}; auto debtTotalProxy = brokerSle->at(sfDebtTotal); + auto const vaultScale = getAssetsTotalScale(vaultSle); + // Send the broker fee to the owner if they have sufficient cover available, // _and_ if the owner can receive funds // _and_ if the broker is authorized to hold funds. If not, so as not to @@ -320,14 +322,22 @@ LoanPay::doApply() // Normally freeze status is checked in preclaim, but we do it here to // avoid duplicating the check. It'll claim a fee either way. bool const sendBrokerFeeToOwner = [&]() { - // Round the minimum required cover up to be conservative. This ensures - // CoverAvailable never drops below the theoretical minimum, protecting - // the broker's solvency. - NumberRoundModeGuard const mg(Number::RoundingMode::Upward); - return coverAvailableProxy >= - roundToAsset( - asset, tenthBipsOfValue(debtTotalProxy.value(), coverRateMinimum), loanScale) && - !isDeepFrozen(view, brokerOwner, asset) && + // In the fixCleanup3_2_0 path, vault-related values (for example, + // DebtTotal) use vaultScale. The legacy path below intentionally retains + // its pre-amendment loanScale behavior. + auto const minCover = [&]() { + if (view.rules().enabled(fixCleanup3_2_0)) + { + return minimumBrokerCover(debtTotalProxy.value(), coverRateMinimum, vaultSle); + } + // Round the minimum required cover up to be conservative. This ensures + // CoverAvailable never drops below the theoretical minimum, protecting + // the broker's solvency. + NumberRoundModeGuard const mg(Number::RoundingMode::Upward); + return roundToAsset( + asset, tenthBipsOfValue(debtTotalProxy.value(), coverRateMinimum), loanScale); + }(); + return coverAvailableProxy >= minCover && !isDeepFrozen(view, brokerOwner, asset) && !requireAuth(view, asset, brokerOwner, AuthType::StrongAuth); }(); @@ -423,10 +433,6 @@ LoanPay::doApply() auto assetsAvailableProxy = vaultSle->at(sfAssetsAvailable); auto assetsTotalProxy = vaultSle->at(sfAssetsTotal); - // The vault may be at a different scale than the loan. Reduce rounding - // errors during the payment by rounding some of the values to that scale. - auto const vaultScale = getAssetsTotalScale(vaultSle); - auto const totalPaidToVaultRaw = paymentParts->principalPaid + paymentParts->interestPaid; auto const totalPaidToVaultRounded = roundToAsset(asset, totalPaidToVaultRaw, vaultScale, Number::RoundingMode::Downward); diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index a3b71fd20d..561ab6b2aa 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -493,11 +493,19 @@ LoanSet::doApply() } TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)}; { - // Round the minimum required cover up to be conservative. This ensures - // CoverAvailable never drops below the theoretical minimum, protecting - // the broker's solvency. - NumberRoundModeGuard const mg(Number::RoundingMode::Upward); - if (brokerSle->at(sfCoverAvailable) < tenthBipsOfValue(newDebtTotal, coverRateMinimum)) + auto const minCover = [&]() { + if (ctx_.view().rules().enabled(fixCleanup3_2_0)) + { + return minimumBrokerCover(newDebtTotal, coverRateMinimum, vaultSle); + } + + // Round the minimum required cover up to be conservative. This ensures + // CoverAvailable never drops below the theoretical minimum, protecting + // the broker's solvency. + NumberRoundModeGuard const mg(Number::RoundingMode::Upward); + return tenthBipsOfValue(newDebtTotal, coverRateMinimum); + }(); + if (brokerSle->at(sfCoverAvailable) < minCover) { JLOG(j_.warn()) << "Insufficient first-loss capital to cover the loan."; return tecINSUFFICIENT_FUNDS; diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index b5687265ab..5e8e89cefa 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -7858,6 +7858,414 @@ protected: to_string(tolerance)); } + // Verify that LoanPay, LoanBrokerCoverWithdraw, and LoanSet all use the + // same vault-scale minimum cover when fixCleanup3_2_0 is enabled. + // Before the amendment, each transactor computed its minimum cover at a + // different precision (loanScale, debtScale, or the raw unrounded + // tenthBipsOfValue), which could lead to inconsistent decisions for the + // same broker state. After the amendment all three use + // minimumBrokerCover at vaultScale. + void + testMinimumBrokerCoverConsistency(FeatureBitset features) + { + using namespace jtx; + using namespace loan; + using namespace loanBroker; + + bool const withAmendment = features[fixCleanup3_2_0]; + + struct Ctx + { + jtx::Account issuer; + jtx::Account lender; + jtx::Account borrower; + jtx::PrettyAsset iou; + BrokerInfo broker; + BrokerParameters brokerParams; + }; + + // Shared setup, parametrized by vaultDeposit (the only varying setup + // field across the three scenarios). Each call runs in its own Env + // so multiple invocations within one scenario cannot interfere. + // The caller is responsible for invoking testcase(...) before the + // first runTest call of each scenario. + auto runTest = [&](Number vaultDeposit, auto&& body) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const lender{"lender"}; + Account const borrower{"borrower"}; + + env.fund(XRP(1'000'000'000), issuer, lender, borrower); + env.close(); + + // Enable clawback on the issuer *before* any trust lines exist + // (asfAllowTrustLineClawback requires an empty owner directory). + env(fset(issuer, asfAllowTrustLineClawback)); + env.close(); + + PrettyAsset const iou = issuer[iouCurrency_]; + env(trust(lender, iou(1'000'000'000))); + env(trust(borrower, iou(1'000'000'000))); + env.close(); + env(pay(issuer, lender, iou(100'000'000))); + env(pay(issuer, borrower, iou(100'000'000))); + env.close(); + + // 13.37% — non-round rate produces a messier minimum. + BrokerParameters const brokerParams{ + .vaultDeposit = vaultDeposit, + .debtMax = 0, + .coverRateMin = TenthBips32{13'370}, + .coverDeposit = 5'000, + .managementFeeRate = TenthBips16{500}}; + + BrokerInfo const broker = createVaultAndBroker(env, iou, lender, brokerParams); + + body( + env, + Ctx{.issuer = issuer, + .lender = lender, + .borrower = borrower, + .iou = iou, + .broker = broker, + .brokerParams = brokerParams}); + }; + + // Scenario 1 — LoanPay + // + // Verify that LoanPay's minimum cover check uses vault scale (not + // loan scale). Before the amendment, different loans could produce + // different fee routing decisions for the same broker-level state. + // Small vault deposit => vaultScale = -12. + testcase("LoanPay minimum cover scale consistency"); + { + struct LoanKeylets + { + Keylet tiny; + Keylet big; + }; + + // Create the tiny + big loans and reduce cover via clawback so + // that subsequent LoanPay calls hit the minimum-cover boundary. + // Used by the two pay-and-check sub-tests below so each can run + // in its own Env. + auto setupLoansAndClawback = [&](Env& env, Ctx const& c) -> std::optional { + Asset const asset{c.iou}; + + // Create the TINY loan first (while vaultScale is still + // small). principal 0.01, 0% interest, 1 payment => + // loanScale = vaultScale. + auto const brokerSle1 = env.le(keylet::loanbroker(c.broker.brokerID)); + if (!BEAST_EXPECT(brokerSle1)) + return std::nullopt; + auto const tinyLoanSeq = brokerSle1->at(sfLoanSequence); + auto const tinyLoanKeylet = keylet::loan(c.broker.brokerID, tinyLoanSeq); + + env(set(c.borrower, c.broker.brokerID, Number{1, -2}), + Sig(sfCounterpartySignature, c.lender), + kInterestRate(TenthBips32{0}), + kPaymentTotal(1), + kPaymentInterval(86400 * 365), + Fee(XRP(10))); + env.close(); + + // Create the BIG loan second. 100% annual interest over 20 + // payments pushes totalValueOutstanding high enough that + // loanScale > vaultScale. + auto const brokerSle2 = env.le(keylet::loanbroker(c.broker.brokerID)); + if (!BEAST_EXPECT(brokerSle2)) + return std::nullopt; + auto const bigLoanSeq = brokerSle2->at(sfLoanSequence); + auto const bigLoanKeylet = keylet::loan(c.broker.brokerID, bigLoanSeq); + + env(set(c.borrower, c.broker.brokerID, Number{500}), + Sig(sfCounterpartySignature, c.lender), + kInterestRate(TenthBips32{100'000}), + kPaymentTotal(20), + kPaymentInterval(86400 * 365), + Fee(XRP(10))); + env.close(); + + // The tiny loan's scale is frozen at the vault's pre-big-loan + // scale, so it is strictly smaller than the big loan's. + // After the big loan is created the vault absorbs its value, + // pushing vaultScale up to match bigLoanScale. + auto const tinyLoanSle = env.le(tinyLoanKeylet); + auto const bigLoanSle = env.le(bigLoanKeylet); + auto const vaultSle = env.le(keylet::vault(c.broker.vaultID)); + if (!BEAST_EXPECT(tinyLoanSle) || !BEAST_EXPECT(bigLoanSle) || + !BEAST_EXPECT(vaultSle)) + return std::nullopt; + if (!BEAST_EXPECT(tinyLoanSle->at(sfLoanScale) == -12) || + !BEAST_EXPECT(bigLoanSle->at(sfLoanScale) == -11) || + !BEAST_EXPECT(getAssetsTotalScale(vaultSle) == -11)) + return std::nullopt; + + // Use issuer clawback to reduce cover to the minimum the + // clawback transactor allows. Compute the amount as + // initialCover - expectedCoverAfter so we exercise the exact + // clawback rather than relying on the transactor to clip + // down. + // + // Before the amendment the clawback minimum is the + // *unrounded* tenthBipsOfValue — strictly less than the + // rounded-at-vaultScale minimum LoanPay uses for the big + // loan. After the amendment both clawback and LoanPay use + // the same rounded minimum (via minimumBrokerCover), so + // cover lands exactly at that threshold. + Number const expectedCoverAfter = withAmendment ? Number{1330651855688460000, -15} + : Number{1330651855688458000, -15}; + Number const clawbackAmount = + Number{c.brokerParams.coverDeposit} - expectedCoverAfter; + + env(coverClawback(c.issuer), + kLoanBrokerId(c.broker.brokerID), + kAmount(STAmount{asset, clawbackAmount})); + env.close(); + + auto const brokerSle = env.le(keylet::loanbroker(c.broker.brokerID)); + if (!BEAST_EXPECT(brokerSle) || + !BEAST_EXPECT(brokerSle->at(sfCoverAvailable) == expectedCoverAfter)) + return std::nullopt; + + return LoanKeylets{.tiny = tinyLoanKeylet, .big = bigLoanKeylet}; + }; + + // Pay one loan and report whether the fee went to the broker's + // pseudo account (the fallback when cover < minimum) rather + // than to the owner. + auto feeGoesToPseudo = [&](Env& env, Ctx const& c, Keylet const& loanKeylet) -> bool { + Asset const asset{c.iou}; + auto const brokerSle = env.le(keylet::loanbroker(c.broker.brokerID)); + if (!BEAST_EXPECT(brokerSle)) + return false; + auto const pseudoAcct = Account("pseudo", brokerSle->at(sfAccount)); + auto const pseudoBefore = env.balance(pseudoAcct, c.iou); + + auto const payLoan = env.le(loanKeylet); + if (!BEAST_EXPECT(payLoan)) + return false; + auto const periodicPayment = payLoan->at(sfPeriodicPayment); + auto const serviceFee = payLoan->at(sfLoanServiceFee); + std::int32_t const loanScale = payLoan->at(sfLoanScale); + + auto const payment = roundPeriodicPayment(asset, periodicPayment, loanScale); + auto const payAmt = STAmount{asset, payment + serviceFee}; + + env(loan::pay(c.borrower, loanKeylet.key, payAmt), Fee(XRP(10))); + env.close(); + + auto const pseudoAfter = env.balance(pseudoAcct, c.iou); + return pseudoAfter.number() > pseudoBefore.number(); + }; + + // Pay the BIG loan in its own Env so its outcome cannot affect + // the TINY-loan check. With the fix, LoanPay and clawback use + // the same vaultScale minimum (cover == minAtVaultScale => + // fee to owner). Without the fix, LoanPay uses bigLoanScale=-11, + // rounds up to a larger minimum than what clawback used => + // cover < min => fee to pseudo. + runTest(/*vaultDeposit=*/1'000, [&](Env& env, Ctx const& c) { + auto const loans = setupLoansAndClawback(env, c); + if (!loans) + return; + BEAST_EXPECT(feeGoesToPseudo(env, c, loans->big) == !withAmendment); + }); + + // Pay the TINY loan in its own Env. Fee goes to the owner + // either way: + // - With the fix: LoanPay uses vaultScale=-11 (same as + // clawback) => owner. + // - Without the fix: LoanPay uses tinyLoanScale=-12, rounds + // up at -12 (a no-op) => min == cover => owner. + runTest(/*vaultDeposit=*/1'000, [&](Env& env, Ctx const& c) { + auto const loans = setupLoansAndClawback(env, c); + if (!loans) + return; + BEAST_EXPECT(!feeGoesToPseudo(env, c, loans->tiny)); + }); + } + + // Scenario 2 — LoanBrokerCoverWithdraw + // + // Verify that CoverWithdraw's minimum cover check uses vault scale + // (not scale(debtTotal, asset)). Before the amendment, CoverWithdraw + // used: + // roundToAsset(asset, tenthBipsOfValue(debt, rate), scale(debt, asset)) + // which could disagree with LoanPay's minimum (which used loanScale). + // + // Use a large vault deposit so that vaultScale (from AssetsTotal) is + // strictly larger than debtScale (from DebtTotal). With + // vaultDeposit = 100,000: after the big loan + // AssetsTotal ≈ 109,500 → vaultScale = -10 + // DebtTotal ≈ 10,000 → debtScale = -11 + // The one-order-of-magnitude gap makes roundToAsset at -10 truncate + // more aggressively than at -11, exposing the bug. + testcase("CoverWithdraw minimum cover scale consistency"); + runTest( + /*vaultDeposit=*/100'000, [&](Env& env, Ctx const& c) { + Asset const asset{c.iou}; + + // Create only the big loan to push DebtTotal up to ~10,000 + // while AssetsTotal stays around 109,500 (dominated by the + // large vault deposit). + env(set(c.borrower, c.broker.brokerID, Number{500}), + Sig(sfCounterpartySignature, c.lender), + kInterestRate(TenthBips32{100'000}), + kPaymentTotal(20), + kPaymentInterval(86400 * 365), + Fee(XRP(10))); + env.close(); + + // Read broker state and compute both old and new minimums. + auto const brokerSle = env.le(keylet::loanbroker(c.broker.brokerID)); + auto const vaultSle = env.le(keylet::vault(c.broker.vaultID)); + if (!BEAST_EXPECT(brokerSle) || !BEAST_EXPECT(vaultSle)) + return; + + auto const coverAvail = brokerSle->at(sfCoverAvailable); + auto const debtTotal = brokerSle->at(sfDebtTotal); + auto const vaultScale = getAssetsTotalScale(vaultSle); + auto const debtScale = scale(debtTotal, asset); + + // Sanity: debt scale differs from vault scale for this setup. + BEAST_EXPECT(debtScale < vaultScale); + + auto const oldMin = [&]() { + NumberRoundModeGuard const mg(Number::RoundingMode::Upward); + return roundToAsset( + asset, + tenthBipsOfValue(debtTotal, TenthBips32{c.brokerParams.coverRateMin}), + debtScale); + }(); + auto const newMin = minimumBrokerCover( + debtTotal, TenthBips32{c.brokerParams.coverRateMin}, vaultSle); + + // The new (vaultScale) minimum must be strictly larger than + // the old (debtScale) minimum — that is the gap the amendment + // closes. + Number const expectedNewMin{1330650518688500000, -15}; + Number const expectedOldMin{1330650518688472000, -15}; + BEAST_EXPECT(newMin == expectedNewMin); + BEAST_EXPECT(oldMin == expectedOldMin); + + // Try to withdraw so that remaining cover lands between the + // two minimums: oldMin < target < newMin. + auto const target = oldMin + (newMin - oldMin) / 2; + auto const withdrawAmount = STAmount{asset, coverAvail - target}; + + if (withAmendment) + { + // CoverWithdraw now uses vaultScale: target < newMin + // => FAILS. + env(coverWithdraw(c.lender, c.broker.brokerID, withdrawAmount), + Ter(tecINSUFFICIENT_FUNDS)); + } + else + { + // Old CoverWithdraw uses debtScale: target > oldMin + // => SUCCEEDS. + env(coverWithdraw(c.lender, c.broker.brokerID, withdrawAmount)); + } + env.close(); + }); + + // Scenario 3 — LoanSet + // + // Verify that LoanSet's minimum cover check uses vault scale (not the + // raw unrounded tenthBipsOfValue). Before the amendment, LoanSet + // used tenthBipsOfValue(newDebtTotal, coverRateMinimum) (no + // roundToAsset), while clawback/withdraw used different formulas. + // After the amendment all use minimumBrokerCover at vaultScale, and + // rounding at a coarser scale can absorb a tiny debt increase — + // allowing a loan that would otherwise be rejected. + testcase("LoanSet minimum cover scale consistency"); + runTest( + /*vaultDeposit=*/1'000, [&](Env& env, Ctx const& c) { + // Create the tiny loan (scale -12) AND the big loan (scale + // -11). Both loans are needed so that DebtTotal has a full + // 16-digit mantissa — a "messy" value where roundToAsset at + // vaultScale actually truncates digits and produces a + // different result from the raw tenthBipsOfValue. With only + // the big loan, DebtTotal has ~4 significant digits and + // rounding at scale -11 is a no-op, masking the amendment's + // effect. + env(set(c.borrower, c.broker.brokerID, Number{1, -2}), + Sig(sfCounterpartySignature, c.lender), + kInterestRate(TenthBips32{0}), + kPaymentTotal(1), + kPaymentInterval(86400 * 365), + Fee(XRP(10))); + env.close(); + + env(set(c.borrower, c.broker.brokerID, Number{500}), + Sig(sfCounterpartySignature, c.lender), + kInterestRate(TenthBips32{100'000}), + kPaymentTotal(20), + kPaymentInterval(86400 * 365), + Fee(XRP(10))); + env.close(); + + // Clawback to reduce cover to the clawback transactor's + // minimum. Pass the exact amount rather than relying on the + // transactor to clip down; the setup matches Scenario 1 so + // the same residual-cover values apply. + Number const expectedCoverAfter = withAmendment ? Number{1330651855688460000, -15} + : Number{1330651855688458000, -15}; + Number const clawbackAmount = + Number{c.brokerParams.coverDeposit} - expectedCoverAfter; + env(coverClawback(c.issuer), + kLoanBrokerId(c.broker.brokerID), + kAmount(c.iou(clawbackAmount))); + env.close(); + + // Verify scales. + auto const vaultSle = env.le(keylet::vault(c.broker.vaultID)); + if (!BEAST_EXPECT(vaultSle)) + return; + auto const vaultScale = getAssetsTotalScale(vaultSle); + BEAST_EXPECT(vaultScale == -11); + + // Now try to create a tiny additional loan. Principal is + // 1e-11 (the smallest value that survives the precision + // check at loanScale = vaultScale = -11), with 0% interest + // and 1 payment. + // + // The tiny debt increase adds ~1.337e-12 to the unrounded + // minimum. + // - Without the amendment: the old LoanSet formula rounds + // up during tenthBipsOfValue (16-digit Number + // normalisation), pushing the minimum past the cover left + // by clawback => tecINSUFFICIENT_FUNDS. + // - With the amendment: minimumBrokerCover rounds at + // vaultScale=-11, which absorbs the tiny increase — the + // rounded minimum stays the same => tesSUCCESS. + auto const tinyPrincipal = Number{1, -11}; + + if (withAmendment) + { + env(set(c.borrower, c.broker.brokerID, tinyPrincipal), + Sig(sfCounterpartySignature, c.lender), + kInterestRate(TenthBips32{0}), + kPaymentTotal(1), + kPaymentInterval(86400 * 365), + Fee(XRP(10))); + } + else + { + env(set(c.borrower, c.broker.brokerID, tinyPrincipal), + Sig(sfCounterpartySignature, c.lender), + kInterestRate(TenthBips32{0}), + kPaymentTotal(1), + kPaymentInterval(86400 * 365), + Fee(XRP(10)), + Ter(tecINSUFFICIENT_FUNDS)); + } + env.close(); + }); + } + void runAmendmentIndependent() { @@ -7919,6 +8327,7 @@ protected: testOverpaymentManagementFee(features); testIssuerIsBorrower(features); testIntegerScalePrincipalSticks(features); + testMinimumBrokerCoverConsistency(features); // RIPD regressions testRIPD3831(features); From e9d885bd9b8d7b6f0fb0fbcd3db7ff39df34e68b Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Tue, 26 May 2026 14:50:18 +0100 Subject: [PATCH 24/87] fix: Fix clang-tidy pre-commit hook to locate compile_commands.json from repo root (#7325) Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> --- .github/scripts/levelization/generate.py | 0 bin/pre-commit/clang_tidy_check.py | 8 +++++++- 2 files changed, 7 insertions(+), 1 deletion(-) mode change 100644 => 100755 .github/scripts/levelization/generate.py diff --git a/.github/scripts/levelization/generate.py b/.github/scripts/levelization/generate.py old mode 100644 new mode 100755 diff --git a/bin/pre-commit/clang_tidy_check.py b/bin/pre-commit/clang_tidy_check.py index 7fb51d1c46..f134660671 100755 --- a/bin/pre-commit/clang_tidy_check.py +++ b/bin/pre-commit/clang_tidy_check.py @@ -168,7 +168,13 @@ def main(): if not os.environ.get("TIDY"): return 0 - repo_root = Path(__file__).parent.parent + repo_root = Path( + subprocess.check_output( + ["git", "rev-parse", "--show-toplevel"], + cwd=Path(__file__).parent, + text=True, + ).strip() + ) files = staged_files(repo_root) if not files: return 0 From 22a21b175efbf95a3435360ba5d6a85ffbfcbcd2 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 26 May 2026 16:01:52 +0200 Subject: [PATCH 25/87] fix: Include management-fee delta in doOverpayment assertion (#7039) --- src/libxrpl/ledger/helpers/LendingHelpers.cpp | 117 ++++++++++------- src/test/app/Loan_test.cpp | 118 ++++++++++++++---- 2 files changed, 168 insertions(+), 67 deletions(-) diff --git a/src/libxrpl/ledger/helpers/LendingHelpers.cpp b/src/libxrpl/ledger/helpers/LendingHelpers.cpp index 1fedbb5f13..9cda5905c9 100644 --- a/src/libxrpl/ledger/helpers/LendingHelpers.cpp +++ b/src/libxrpl/ledger/helpers/LendingHelpers.cpp @@ -769,9 +769,6 @@ doOverpayment( "xrpl::detail::doOverpayment", "principal change agrees"); - // I'm not 100% sure the following asserts are correct. If in doubt, and - // everything else works, remove any that cause trouble. - JLOG(j.debug()) << "valueChange: " << loanPaymentParts.valueChange << ", totalValue before: " << *totalValueOutstandingProxy << ", totalValue after: " << newRoundedLoanState.valueOutstanding @@ -783,11 +780,28 @@ doOverpayment( << overpaymentComponents.trackedPrincipalDelta - (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding); + // The valueChange returned by tryOverpayment satisfies + // valueChange = (newInterestDue - oldInterestDue) + untrackedInterest. + // Using the loan-state identity v = p + i + m and the adjacent + // `principal change agrees` assertion (dp = oldP - newP), this + // rearranges into three independently-computable terms: + // + // 1. TVO change beyond what principal repayment alone explains: + // newTVO - (oldTVO - dp) + // 2. Management fee released by re-amortization (positive when + // mfee decreased; zero when managementFeeRate == 0): + // oldMfee - newMfee + // 3. The overpayment's penalty interest part (= untrackedInterest + // for the overpayment path; see computeOverpaymentComponents): + // trackedInterestPart() + [[maybe_unused]] Number const tvoChange = newRoundedLoanState.valueOutstanding - + (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta); + [[maybe_unused]] Number const managementFeeReleased = + managementFeeOutstandingProxy - newRoundedLoanState.managementFeeDue; + [[maybe_unused]] Number const interestPart = overpaymentComponents.trackedInterestPart(); + XRPL_ASSERT_PARTS( - loanPaymentParts.valueChange == - newRoundedLoanState.valueOutstanding - - (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta) + - overpaymentComponents.trackedInterestPart(), + loanPaymentParts.valueChange == tvoChange + managementFeeReleased + interestPart, "xrpl::detail::doOverpayment", "interest paid agrees"); @@ -2027,51 +2041,62 @@ loanMakePayment( // It shouldn't be possible for the overpayment to be greater than // totalValueOutstanding, because that would have been processed as // another normal payment. But cap it just in case. - Number const overpayment = std::min(roundedAmount - totalPaid, *totalValueOutstandingProxy); + Number const overpaymentRaw = + std::min(roundedAmount - totalPaid, *totalValueOutstandingProxy); - detail::ExtendedPaymentComponents const overpaymentComponents = - detail::computeOverpaymentComponents( - asset, - loanScale, - overpayment, - overpaymentInterestRate, - overpaymentFeeRate, - managementFeeRate); + bool const fixEnabled = view.rules().enabled(fixCleanup3_2_0); + Number const overpayment = fixEnabled + ? roundToAsset(asset, overpaymentRaw, loanScale, Number::RoundingMode::Downward) + : overpaymentRaw; - // Don't process an overpayment if the whole amount (or more!) - // gets eaten by fees and interest. - if (overpaymentComponents.trackedPrincipalDelta > 0) + // Post-amendment, the rounded overpayment can be zero; pre-amendment + // it's always positive given the surrounding guards. + if (!fixEnabled || overpayment > 0) { - XRPL_ASSERT_PARTS( - overpaymentComponents.untrackedInterest >= beast::kZero, - "xrpl::loanMakePayment", - "overpayment penalty did not reduce value of loan"); - // Can't just use `periodicPayment` here, because it might - // change - auto periodicPaymentProxy = loan->at(sfPeriodicPayment); - if (auto const overResult = detail::doOverpayment( - view.rules(), + detail::ExtendedPaymentComponents const overpaymentComponents = + detail::computeOverpaymentComponents( asset, loanScale, - overpaymentComponents, - totalValueOutstandingProxy, - principalOutstandingProxy, - managementFeeOutstandingProxy, - periodicPaymentProxy, - periodicRate, - paymentRemainingProxy, - managementFeeRate, - j)) + overpayment, + overpaymentInterestRate, + overpaymentFeeRate, + managementFeeRate); + + // Don't process an overpayment if the whole amount (or more!) + // gets eaten by fees and interest. + if (overpaymentComponents.trackedPrincipalDelta > 0) { - totalParts += *overResult; - } - else if (overResult.error()) - { - // error() will be the TER returned if a payment is not - // made. It will only evaluate to true if it's unsuccessful. - // Otherwise, tesSUCCESS means nothing was done, so - // continue. - return Unexpected(overResult.error()); + XRPL_ASSERT_PARTS( + overpaymentComponents.untrackedInterest >= beast::kZero, + "xrpl::loanMakePayment", + "overpayment penalty did not reduce value of loan"); + // Can't just use `periodicPayment` here, because it might + // change + auto periodicPaymentProxy = loan->at(sfPeriodicPayment); + if (auto const overResult = detail::doOverpayment( + view.rules(), + asset, + loanScale, + overpaymentComponents, + totalValueOutstandingProxy, + principalOutstandingProxy, + managementFeeOutstandingProxy, + periodicPaymentProxy, + periodicRate, + paymentRemainingProxy, + managementFeeRate, + j)) + { + totalParts += *overResult; + } + else if (overResult.error()) + { + // error() will be the TER returned if a payment is not + // made. It will only evaluate to true if it's unsuccessful. + // Otherwise, tesSUCCESS means nothing was done, so + // continue. + return Unexpected(overResult.error()); + } } } } diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 5e8e89cefa..c380655563 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -169,6 +169,10 @@ protected: TenthBips32 coverRateLiquidation = percentageToTenthBips(25); std::string data = {}; // NOLINT(readability-redundant-member-init) std::uint32_t flags = 0; + // If set, the vault is created with this sfScale value. Useful for + // tests that need finer loanScale to exercise rounding edge cases. + std::optional vaultScale = + std::nullopt; // NOLINT(readability-redundant-member-init) [[nodiscard]] Number maxCoveredLoanValue(Number const& currentDebt) const @@ -522,6 +526,8 @@ protected: auto const coverRateMinValue = params.coverRateMin; auto [tx, vaultKeylet] = vault.create({.owner = lender, .asset = asset}); + if (params.vaultScale) + tx[sfScale] = *params.vaultScale; env(tx); env.close(); BEAST_EXPECT(env.le(vaultKeylet)); @@ -2157,21 +2163,23 @@ protected: // If the loan does not allow overpayments, send a payment that // tries to make an overpayment. Do not include `txFlags`, so we // don't end up duplicating the next test transaction. - env(pay(borrower, - loanKeylet.key, - STAmount{broker.asset, state.periodicPayment * Number{15, -1}}, - tfLoanOverpayment), - Fee(XRPAmount{baseFee * (Number{15, -1} / kLoanPaymentsPerFeeIncrement + 1)}), - Ter(tecNO_PERMISSION)); + // + // fixCleanup3_1_3 gates tfLoanOverpayment as a valid flag: + // with fix on → preflight passes, apply returns tecNO_PERMISSION; + // with fix off → preflight rejects the flag, returns temINVALID_FLAG. + bool const hasFix313 = env.current()->rules().enabled(fixCleanup3_1_3); + STAmount const overpayAmount{broker.asset, state.periodicPayment * Number{15, -1}}; + XRPAmount const overpayFee{ + baseFee * (Number{15, -1} / kLoanPaymentsPerFeeIncrement + 1)}; + env(pay(borrower, loanKeylet.key, overpayAmount, tfLoanOverpayment), + Fee(overpayFee), + Ter(hasFix313 ? TER{tecNO_PERMISSION} : TER{temINVALID_FLAG})); + if (hasFix313) { env.disableFeature(fixCleanup3_1_3); - env(pay(borrower, - loanKeylet.key, - STAmount{broker.asset, state.periodicPayment * Number{15, -1}}, - tfLoanOverpayment), - Fee(XRPAmount{ - baseFee * (Number{15, -1} / kLoanPaymentsPerFeeIncrement + 1)}), + env(pay(borrower, loanKeylet.key, overpayAmount, tfLoanOverpayment), + Fee(overpayFee), Ter(temINVALID_FLAG)); env.enableFeature(fixCleanup3_1_3); } @@ -7027,7 +7035,7 @@ protected: auto credType = "credential1"; - pdomain::Credentials const credentials1{{.issuer = issuer, .credType = credType}}; + pdomain::Credentials const credentials1 = {{.issuer = issuer, .credType = credType}}; env(pdomain::setTx(issuer, credentials1)); env.close(); @@ -7572,6 +7580,74 @@ protected: attemptWithdrawShares(depositorB, sharesLpB, tesSUCCESS); } + // An overpayment whose residual amount has more precision than loanScale + // fires the isRounded(asset, overpayment, loanScale) assertion in + // computeOverpaymentComponents (and a downstream "interest paid agrees" + // assertion in doOverpayment). fixCleanup3_2_0 rounds the residual down + // to loanScale before passing it in. The pre-amendment path can't be + // tested here because the assertion fires in Debug builds and aborts + // the test process — see the PR description for context. + void + testBugOverpayUnroundedAmount() + { + testcase("bug: computeOverpaymentComponents isRounded assertion"); + + using namespace jtx; + using namespace loan; + Env env(*this, all_); + + Account const issuer{"issuer"}; + Account const lender{"vaultOwner"}; + Account const borrower{"borrower"}; + + env.fund(XRP(1'000'000), issuer, lender, borrower); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const iouAsset = issuer["USD"]; + STAmount const iouLimit{iouAsset.raw(), Number{9'999'999'999'999'999LL}}; + env(trust(lender, iouLimit)); + env(trust(borrower, iouLimit)); + env(pay(issuer, lender, iouAsset(1'000'000))); + env(pay(issuer, borrower, iouAsset(1'000'000))); + env.close(); + + auto const broker = createVaultAndBroker( + env, + iouAsset, + lender, + {.vaultDeposit = 100'000, + .debtMax = 5000, + .managementFeeRate = TenthBips16{1000}, + .vaultScale = 1}); + + auto const sleBroker = env.le(broker.brokerKeylet()); + if (!BEAST_EXPECT(sleBroker)) + return; + auto const loanSequence = sleBroker->at(sfLoanSequence); + auto const loanKeylet = keylet::loan(broker.brokerID, loanSequence); + + using namespace loan; + env(set(borrower, broker.brokerID, Number{1000}, tfLoanOverpayment), + Sig(sfCounterpartySignature, lender), + kInterestRate(TenthBips32{10000}), + kPaymentTotal(12), + kPaymentInterval(60), + kGracePeriod(60), + kOverpaymentFee(TenthBips32{1000}), + kOverpaymentInterestRate(TenthBips32{1000}), + Fee(env.current()->fees().base * 2), + Ter(tesSUCCESS)); + env.close(); + + // periodic * 1.5 at 15-sig-digit precision: 125.000154585042. This + // has too many digits to round cleanly to loanScale=-10, so the + // overpayment residual fails the isRounded check. + STAmount const payAmount{iouAsset.raw(), Number{125'000'154'585'042LL, -12}}; + env(pay(borrower, loanKeylet.key, payAmount), Txflags(tfLoanOverpayment), Ter(tesSUCCESS)); + env.close(); + } + // Regression for the dual-rounding fix at coarse (integer-MPT) scale. // // Loan: P=1, r=50% (50000 tenth-bips), n=3, yearly interval. The @@ -8280,6 +8356,9 @@ protected: testRIPD3901(); testBorrowerIsBroker(); testLimitExceeded(); + testLoanSetBlockedLoanPayAllowedWhenCanTransferCleared(); + testLendingCanTradeClearedNoImpact(); + testBugOverpayUnroundedAmount(); for (auto const flags : {0u, tfLoanOverpayment}) testYieldTheftRounding(flags); @@ -8295,11 +8374,11 @@ protected: testLoanPayLateFullPaymentBypassesPenalties(features); testLoanCoverMinimumRoundingExploit(features); #endif - // Lifecycle - testSelfLoan(features); - testLoanSet(features); testLifecycle(features); + testLoanSet(features); + testDosLoanPay(features); + testSelfLoan(features); // Payment paths testWithdrawReflectsUnrealizedLoss(features); @@ -8346,11 +8425,8 @@ public: run() override { runAmendmentIndependent(); - testLoanSetBlockedLoanPayAllowedWhenCanTransferCleared(); - testLendingCanTradeClearedNoImpact(); - testDosLoanPay(all_ | fixCleanup3_1_3); - testDosLoanPay(all_ - fixCleanup3_1_3); - for (auto const& features : amendmentCombinations({fixCleanup3_2_0, featureMPTokensV2})) + for (auto const& features : + amendmentCombinations({fixCleanup3_1_3, fixCleanup3_2_0, featureMPTokensV2})) runAmendmentSensitive(features); } }; From 49cb3f45a42d1f050212e1653a663789fb592d2a Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 26 May 2026 16:45:33 +0100 Subject: [PATCH 26/87] ci: Add clang to nix images (#7308) Co-authored-by: semgrep-companion-app[bot] <218312740+semgrep-companion-app[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/build-nix-image.yml | 114 ++++++++++-------- .../workflows/reusable-build-docker-image.yml | 89 ++++++++++++++ cspell.config.yaml | 3 + docker/check-sanitizers.sh | 42 +++++++ docker/cpp_files/asan.cpp | 28 +++++ docker/cpp_files/tsan.cpp | 26 ++++ docker/cpp_files/ubsan.cpp | 13 ++ docker/nix.Dockerfile | 29 +++++ flake.lock | 4 +- flake.nix | 6 +- nix/ci-env.nix | 108 +++++++++++++---- nix/packages.nix | 1 + nix/utils.nix | 8 +- 13 files changed, 387 insertions(+), 84 deletions(-) create mode 100644 .github/workflows/reusable-build-docker-image.yml create mode 100755 docker/check-sanitizers.sh create mode 100644 docker/cpp_files/asan.cpp create mode 100644 docker/cpp_files/tsan.cpp create mode 100644 docker/cpp_files/ubsan.cpp diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index 6554cf6c08..a8fb6eec86 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -6,14 +6,16 @@ on: - develop paths: - ".github/workflows/build-nix-image.yml" - - "docker/nix.Dockerfile" + - ".github/workflows/reusable-build-docker-image.yml" + - "docker/**" - "flake.nix" - "flake.lock" - "nix/**" pull_request: paths: - ".github/workflows/build-nix-image.yml" - - "docker/nix.Dockerfile" + - ".github/workflows/reusable-build-docker-image.yml" + - "docker/**" - "flake.nix" - "flake.lock" - "nix/**" @@ -27,75 +29,81 @@ defaults: run: shell: bash -env: - UBUNTU_VERSION: "20.04" - RHEL_VERSION: "9" - DEBIAN_VERSION: "bookworm" - jobs: build: - name: Build and push Nix image (${{ matrix.distro }}) + name: Build ${{ matrix.distro.name }} (${{ matrix.target.platform }}) + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + # The base images are the oldest supported version of each distro + # that we want to build images for. + distro: + - name: nixos + base_image: nixos/nix:latest + - name: ubuntu + base_image: ubuntu:20.04 + - name: rhel + base_image: registry.access.redhat.com/ubi9/ubi:latest + - name: debian + base_image: debian:bookworm + target: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + uses: ./.github/workflows/reusable-build-docker-image.yml + with: + image_name: ghcr.io/xrplf/xrpld/nix-${{ matrix.distro.name }} + dockerfile: docker/nix.Dockerfile + base_image: ${{ matrix.distro.base_image }} + platform: ${{ matrix.target.platform }} + runner: ${{ matrix.target.runner }} + push: ${{ github.event_name == 'push' }} + + merge: + name: Merge ${{ matrix.distro }} manifest + needs: build + if: github.event_name == 'push' runs-on: ubuntu-latest permissions: contents: read packages: write strategy: + fail-fast: false matrix: - include: - - distro: nixos - - distro: ubuntu - - distro: rhel - - distro: debian + distro: [nixos, ubuntu, rhel, debian] + env: + IMAGE_NAME: ghcr.io/xrplf/xrpld/nix-${{ matrix.distro }} steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Determine base image - id: vars - run: | - case "${{ matrix.distro }}" in - nixos) - echo "base_image=nixos/nix:latest" >> $GITHUB_OUTPUT - ;; - ubuntu) - echo "base_image=ubuntu:${UBUNTU_VERSION}" >> $GITHUB_OUTPUT - ;; - rhel) - echo "base_image=registry.access.redhat.com/ubi${RHEL_VERSION}/ubi:latest" >> $GITHUB_OUTPUT - ;; - debian) - echo "base_image=debian:${DEBIAN_VERSION}" >> $GITHUB_OUTPUT - ;; - esac - - name: Set up Docker Buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + - name: Docker metadata + id: meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=sha,prefix=sha-,format=short + type=raw,value=latest + - name: Login to GitHub Container Registry - if: github.event_name == 'push' uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Docker metadata - id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 - with: - images: ghcr.io/xrplf/ci/nix-${{ matrix.distro }} - tags: | - type=sha,prefix=sha-,format=short - type=raw,value=latest + - name: Create multi-arch manifests + run: | + for tag in $(jq -cr '.tags[]' <<< "$DOCKER_METADATA_OUTPUT_JSON"); do + docker buildx imagetools create -t "$tag" "${tag}-amd64" "${tag}-arm64" + done - - name: Build and push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 - with: - context: . - file: docker/nix.Dockerfile - platforms: linux/amd64 - push: ${{ github.event_name == 'push' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: BASE_IMAGE=${{ steps.vars.outputs.base_image }} + - name: Inspect image + run: | + docker buildx imagetools inspect "${IMAGE_NAME}:${{ steps.meta.outputs.version }}" diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml new file mode 100644 index 0000000000..e631e02368 --- /dev/null +++ b/.github/workflows/reusable-build-docker-image.yml @@ -0,0 +1,89 @@ +# Build a single-platform Docker image. On push, the image is pushed to +# GHCR with arch-suffixed tags (e.g. `:latest-amd64`, `:sha-abc-amd64`) +# so the calling workflow can stitch per-arch builds into a multi-arch +# manifest without needing to pass digests around. +name: Reusable build Docker image (single platform) + +on: + workflow_call: + inputs: + image_name: + description: "Full image name without tag (e.g. 'ghcr.io/xrplf/xrpld/nix-ubuntu')" + required: true + type: string + dockerfile: + description: "Path to the Dockerfile, relative to the repository root" + required: true + type: string + base_image: + description: "Value passed to the Dockerfile as the BASE_IMAGE build arg" + required: true + type: string + platform: + description: "Docker platform string, e.g. linux/amd64" + required: true + type: string + runner: + description: "GitHub Actions runner label to build on" + required: true + type: string + push: + description: "Whether to push the image to GHCR" + required: true + type: boolean + +defaults: + run: + shell: bash + +jobs: + build: + name: Build (${{ inputs.platform }}) + runs-on: ${{ inputs.runner }} + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Determine arch + id: vars + env: + PLATFORM: ${{ inputs.platform }} + run: | + echo "arch=${PLATFORM##*/}" >> $GITHUB_OUTPUT + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: Login to GitHub Container Registry + if: inputs.push + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker metadata + id: meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + with: + images: ${{ inputs.image_name }} + tags: | + type=sha,prefix=sha-,format=short + type=raw,value=latest + flavor: | + suffix=-${{ steps.vars.outputs.arch }},onlatest=true + + - name: Build and push + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + with: + context: . + file: ${{ inputs.dockerfile }} + platforms: ${{ inputs.platform }} + push: ${{ inputs.push }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: BASE_IMAGE=${{ inputs.base_image }} diff --git a/cspell.config.yaml b/cspell.config.yaml index 275df41f58..82a6a20158 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -199,11 +199,13 @@ words: - nonxrp - noreplace - noripple + - nostdinc - notifempty - nudb - nullptr - nunl - Nyffenegger + - onlatest - ostr - pargs - partitioner @@ -298,6 +300,7 @@ words: - unauthorizing - unergonomic - unfetched + - unfindable - unflatten - unfund - unimpair diff --git a/docker/check-sanitizers.sh b/docker/check-sanitizers.sh new file mode 100755 index 0000000000..db51f11851 --- /dev/null +++ b/docker/check-sanitizers.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# Sanity-check that the sanitizer runtimes shipped with g++/clang++ work +# end-to-end against the system loader: compile each example with both +# compilers, run it, and confirm the expected diagnostic is emitted. + +set -eo pipefail + +cpp_files_dir="${1:?usage: $0 }" + +case "$(uname -m)" in + x86_64) loader=/lib64/ld-linux-x86-64.so.2 ;; + aarch64) loader=/lib/ld-linux-aarch64.so.1 ;; + *) echo "Unsupported arch: $(uname -m)" >&2; exit 1 ;; +esac + +declare -A sanitize=( + [asan]="-fsanitize=address" + [tsan]="-fsanitize=thread" + [ubsan]="-fsanitize=undefined" +) +declare -A expect=( + [asan]="heap-use-after-free" + [tsan]="data race" + [ubsan]="signed integer overflow" +) + +for compiler in g++ clang++; do + for name in asan tsan ubsan; do + bin="/tmp/${name}-${compiler}" + echo "=== Build ${name} with ${compiler} ===" + "$compiler" -std=c++20 -O1 -g ${sanitize[$name]} \ + -Wl,--dynamic-linker=$loader \ + "${cpp_files_dir}/${name}.cpp" -o "$bin" + echo "=== Run ${name}-${compiler} ===" + output=$("$bin" 2>&1) || true + echo "$output" + echo "$output" | grep -q "${expect[$name]}" \ + || { echo "expected '${expect[$name]}' from $bin"; exit 1; } + rm -f "$bin" + done +done diff --git a/docker/cpp_files/asan.cpp b/docker/cpp_files/asan.cpp new file mode 100644 index 0000000000..8347f58d37 --- /dev/null +++ b/docker/cpp_files/asan.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +#if defined(__clang__) || defined(__GNUC__) +__attribute__((noinline)) +#elif defined(_MSC_VER) +__declspec(noinline) +#endif +int +read_after_free(volatile int* array, std::size_t index) +{ + std::atomic_signal_fence(std::memory_order_seq_cst); + int value = array[index]; + std::atomic_signal_fence(std::memory_order_seq_cst); + return value; +} + +int +main() +{ + int* array = new int[5]{10, 20, 30, 40, 50}; + delete[] array; + + std::cout << "Value at index 2: " << read_after_free(array, 2) << std::endl; + + return 0; +} diff --git a/docker/cpp_files/tsan.cpp b/docker/cpp_files/tsan.cpp new file mode 100644 index 0000000000..34b0990a6d --- /dev/null +++ b/docker/cpp_files/tsan.cpp @@ -0,0 +1,26 @@ +#include +#include + +static int kCounter = 0; + +void +increment() +{ + for (int i = 0; i < 100'000; ++i) + { + ++kCounter; + } +} + +int +main() +{ + std::thread t1(increment); + std::thread t2(increment); + + t1.join(); + t2.join(); + + std::cout << "Final counter value: " << kCounter << std::endl; + return 0; +} diff --git a/docker/cpp_files/ubsan.cpp b/docker/cpp_files/ubsan.cpp new file mode 100644 index 0000000000..db86119070 --- /dev/null +++ b/docker/cpp_files/ubsan.cpp @@ -0,0 +1,13 @@ +#include +#include + +int +main() +{ + int maxInt = std::numeric_limits::max(); + int volatile one = 1; + std::cout << "Current max: " << maxInt << std::endl; + int overflowed = maxInt + one; + std::cout << "Overflowed result: " << overflowed << std::endl; + return 0; +} diff --git a/docker/nix.Dockerfile b/docker/nix.Dockerfile index 52faa8b8dc..690f0b76bd 100644 --- a/docker/nix.Dockerfile +++ b/docker/nix.Dockerfile @@ -45,8 +45,30 @@ COPY --from=builder /tmp/build/result /nix/ci-env ENV PATH="/nix/ci-env/bin:$PATH" +# Externally-built dynamically-linked ELF binaries hard-code the loader path +# (e.g. /lib64/ld-linux-x86-64.so.2) in their PT_INTERP header. Copy the +# loader from the Nix store to that path when the base image doesn't already +# provide one (i.e. on nixos/nix). +RUN <&2; exit 1 ;; +esac +if [ ! -e "$target" ]; then + # Use the loader from the same glibc that gcc links libc against, so + # ld-linux and libc/libpthread share GLIBC_PRIVATE symbols at runtime. + src="$(dirname "$(gcc -print-file-name=libc.so.6)")/$(basename "$target")" + [ -e "$src" ] || { echo "ld-linux not found at $src" >&2; exit 1; } + mkdir -p "$(dirname "$target")" + cp "$src" "$target" +fi +EOF + RUN </dev/null && /tmp/check-sanitizers.sh /tmp/cpp_files || true diff --git a/flake.lock b/flake.lock index 5ec053975d..3149f3feed 100644 --- a/flake.lock +++ b/flake.lock @@ -15,7 +15,7 @@ "type": "indirect" } }, - "nixpkgs-glibc231": { + "nixpkgs-custom-glibc": { "flake": false, "locked": { "lastModified": 1593520194, @@ -35,7 +35,7 @@ "root": { "inputs": { "nixpkgs": "nixpkgs", - "nixpkgs-glibc231": "nixpkgs-glibc231" + "nixpkgs-custom-glibc": "nixpkgs-custom-glibc" } } }, diff --git a/flake.nix b/flake.nix index 18671bdf31..3b3ec7ea08 100644 --- a/flake.nix +++ b/flake.nix @@ -6,16 +6,16 @@ # version — matches the system libc on Ubuntu 20.04 LTS. Imported # manually (flake = false) because this revision predates nixpkgs' # own flake.nix. - nixpkgs-glibc231 = { + nixpkgs-custom-glibc = { url = "github:NixOS/nixpkgs/9cd98386a38891d1074fc18036b842dc4416f562"; flake = false; }; }; outputs = - { nixpkgs, nixpkgs-glibc231, ... }: + { nixpkgs, nixpkgs-custom-glibc, ... }: let - forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-glibc231; }; + forEachSystem = import ./nix/utils.nix { inherit nixpkgs nixpkgs-custom-glibc; }; in { devShells = forEachSystem (import ./nix/devshell.nix); diff --git a/nix/ci-env.nix b/nix/ci-env.nix index d8021fe0bd..0d617913d9 100644 --- a/nix/ci-env.nix +++ b/nix/ci-env.nix @@ -1,39 +1,102 @@ { pkgs, - glibc231, + customGlibc, ... }: let inherit (import ./packages.nix { inherit pkgs; }) commonPackages; + inherit (pkgs) lib; - # binutils wrapped to emit binaries that reference glibc 2.31 (dynamic - # linker path, library search path, RPATH). - binutils231 = pkgs.wrapBintoolsWith { + # Underlying compiler toolchains to wrap. Bump these in one place to + # roll the whole environment forward. + customGccPackage = pkgs.gcc15; + customLlvmPackages = pkgs.llvmPackages_22; + customClangMajor = lib.versions.major (lib.getVersion customLlvmPackages.clang-unwrapped); + + # binutils wrapped to emit binaries that reference the custom glibc + # (dynamic linker path, library search path, RPATH). + customBinutils = pkgs.wrapBintoolsWith { bintools = pkgs.binutils-unwrapped; - libc = glibc231; + libc = customGlibc; }; - # Rebuild gcc 15 (specifically libstdc++ / libgcc_s) against glibc 2.31. - # The override swaps gcc15.cc's bootstrap stdenv for one that uses the - # existing gcc 15 binary but links against glibc 2.31, so the resulting - # compiler ships runtime libraries that only reference symbols available - # in glibc 2.31. - gcc15CcWithGlibc231 = pkgs.gcc15.cc.override { + # Rebuild gcc (specifically libstdc++ / libgcc_s) against the custom + # glibc. The override swaps gcc.cc's bootstrap stdenv for one that uses + # the existing gcc binary but links against the custom glibc, so the + # resulting compiler ships runtime libraries that only reference symbols + # available in that glibc. + customGccCc = customGccPackage.cc.override { stdenv = pkgs.stdenvAdapters.overrideCC pkgs.stdenv ( pkgs.wrapCCWith { - cc = pkgs.gcc15.cc; - libc = glibc231; - bintools = binutils231; + cc = customGccPackage.cc; + libc = customGlibc; + bintools = customBinutils; } ); }; - # cc-wrapper around the rebuilt compiler, pointing at glibc 2.31 headers - # and libraries. This is what we actually expose to users. - gcc15WithGlibc231 = pkgs.wrapCCWith { - cc = gcc15CcWithGlibc231; - libc = glibc231; - bintools = binutils231; + # cc-wrapper around the rebuilt compiler, pointing at the custom glibc + # headers and libraries. This is what we actually expose to users. + customGcc = pkgs.wrapCCWith { + cc = customGccCc; + libc = customGlibc; + bintools = customBinutils; + }; + + # stdenv built around the rebuilt gcc / custom glibc. Used to rebuild + # compiler-rt below so its sanitizer runtimes see the custom glibc + # headers. + customStdenv = pkgs.stdenvAdapters.overrideCC pkgs.stdenv customGcc; + + # Rebuild compiler-rt against the custom glibc so the sanitizer runtimes + # don't use glibc symbols (or sysconf constants like _SC_SIGSTKSZ) that + # only exist in newer glibc versions. scudo is dropped because its CMake + # includes CheckAtomic with -nostdinc++ in CMAKE_REQUIRED_FLAGS, which + # makes std::atomic unfindable in our stdenv; we don't use scudo (only + # asan/ubsan/tsan etc.). + customCompilerRt = + (customLlvmPackages.compiler-rt.override { + stdenv = customStdenv; + }).overrideAttrs + (old: { + postPatch = (old.postPatch or "") + '' + substituteInPlace lib/CMakeLists.txt \ + --replace-quiet 'add_subdirectory(scudo/standalone)' \ + '# scudo/standalone disabled in xrpld ci-env' + ''; + }); + + # cc-wrapper around clang, pointing at the custom glibc headers and + # libraries. Reuses the rebuilt gcc for libstdc++ / libgcc_s so that + # C++ binaries produced by clang also only reference symbols available + # in the custom glibc. compiler-rt is wired into a resource-root so + # sanitizer runtimes (libclang_rt.*.a) are found at link time; this + # mirrors what nixpkgs does internally when building llvmPackages.clang. + customClang = pkgs.wrapCCWith { + cc = customLlvmPackages.clang-unwrapped; + libc = customGlibc; + bintools = customBinutils; + gccForLibs = customGccCc; + extraPackages = [ customCompilerRt ]; + extraBuildCommands = '' + rsrc="$out/resource-root" + mkdir "$rsrc" + ln -s "${customLlvmPackages.clang-unwrapped.lib}/lib/clang/${customClangMajor}/include" "$rsrc/include" + ln -s "${customCompilerRt.out}/lib" "$rsrc/lib" + ln -s "${customCompilerRt.out}/share" "$rsrc/share" || true + echo "-resource-dir=$rsrc" >> $out/nix-support/cc-cflags + ''; + }; + + # Strip the generic cc/c++/cpp symlinks from the clang wrapper so it can + # coexist with the gcc wrapper in buildEnv. gcc remains the default + # compiler (cc/c++/cpp); clang is invoked explicitly as clang/clang++. + customClangForCiEnv = pkgs.symlinkJoin { + name = "clang-wrapper-custom-for-ci-env"; + paths = [ customClang ]; + postBuild = '' + rm -f $out/bin/cc $out/bin/c++ $out/bin/cpp + ''; }; in @@ -41,8 +104,9 @@ in default = pkgs.buildEnv { name = "xrpld-ci-env"; paths = commonPackages ++ [ - gcc15WithGlibc231 - binutils231 + customGcc + customClangForCiEnv + customBinutils ]; pathsToLink = [ "/bin" diff --git a/nix/packages.nix b/nix/packages.nix index edfe302ec9..d209620a68 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -17,6 +17,7 @@ in llvmPackages_22.clang-tools mold ninja + patchelf perl # needed for openssl pkg-config pre-commit diff --git a/nix/utils.nix b/nix/utils.nix index 07ff169c44..d83e612c16 100644 --- a/nix/utils.nix +++ b/nix/utils.nix @@ -1,4 +1,4 @@ -{ nixpkgs, nixpkgs-glibc231 }: +{ nixpkgs, nixpkgs-custom-glibc }: function: nixpkgs.lib.genAttrs [ @@ -12,10 +12,10 @@ nixpkgs.lib.genAttrs function { pkgs = import nixpkgs { inherit system; }; # glibc 2.31 — matches the system libc on Ubuntu 20.04 LTS. Sourced - # from the nixpkgs snapshot pinned via the `nixpkgs-glibc231` flake - # input, so the build uses the compiler from that snapshot + # from the nixpkgs snapshot pinned via the `nixpkgs-custom-glibc` + # flake input, so the build uses the compiler from that snapshot # (gcc 9.3.0) along with the matching patches, configure flags, and # hardening defaults. - glibc231 = (import nixpkgs-glibc231 { inherit system; }).glibc; + customGlibc = (import nixpkgs-custom-glibc { inherit system; }).glibc; } ) From 633ef4706ffdd0bed445032d2de92e86e59985d6 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 26 May 2026 18:32:44 +0200 Subject: [PATCH 27/87] fix: Fix `VaultInvariant` and `VaultDeposit` precision bugs at IOU scale boundaries (#7272) Co-authored-by: Bart --- include/xrpl/protocol/STAmount.h | 1 - include/xrpl/tx/invariants/VaultInvariant.h | 77 ++- src/libxrpl/tx/invariants/VaultInvariant.cpp | 345 ++++++------ .../tx/transactors/vault/VaultDeposit.cpp | 87 +++- src/test/app/Vault_test.cpp | 489 ++++++++++++++++++ src/test/protocol/STAmount_test.cpp | 2 - 6 files changed, 820 insertions(+), 181 deletions(-) diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index bf3e25eedb..a4fffad40c 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -189,7 +189,6 @@ public: /** * Checks if this amount evaluates to zero when constrained to a specific * accounting scale. - * * For XRP and MPT `roundToScale` is a no-op, returns true only when the amount itself is zero. * The `scale` argument is ignored in that case. * For IOU, the amount is rounded to the given scale using Number::RoundingMode::ToNearest mode diff --git a/include/xrpl/tx/invariants/VaultInvariant.h b/include/xrpl/tx/invariants/VaultInvariant.h index ab55cd086a..abc256c880 100644 --- a/include/xrpl/tx/invariants/VaultInvariant.h +++ b/include/xrpl/tx/invariants/VaultInvariant.h @@ -4,9 +4,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -79,16 +81,83 @@ private: std::vector beforeMPTs_; std::unordered_map deltas_; + /** + * @brief Compute the minimum STAmount scale for rounding invariant + * calculations. + * + * Post-amendment (@c fixCleanup3_2_0) this is simply the posterior + * @c assetsTotal scale. Pre-amendment it is the coarsest scale across + * @p vaultDelta and both asset-field deltas. + * + * @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. + */ + [[nodiscard]] std::int32_t + computeVaultMinScale(DeltaInfo const& vaultDelta, Rules const& rules) const; + + /** + * @brief Return the vault-asset balance-change delta for an account. + * + * Looks up the ledger-entry delta recorded during @c visitEntry for the + * account entry (XRP), trust line (IOU), or MPToken (MPT) that corresponds + * 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. + */ + [[nodiscard]] std::optional + deltaAssets(AccountID const& id) const; + + /** + * @brief Return the vault-asset delta for the transaction's sending + * account, adjusted for the fee. + * + * Calls @c deltaAssets for @c tx[sfAccount] and, for non-delegated XRP + * transactions, adds the consumed fee back so the invariant sees the net + * asset movement rather than the fee-reduced balance change. + * + * @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. + */ + [[nodiscard]] std::optional + deltaAssetsTxAccount(STTx const& tx, XRPAmount fee) const; + + /** + * @brief Return the vault-share balance-change delta for an account. + * + * For the vault's pseudo-account the @c MPTokenIssuance outstanding-amount + * delta is returned; for all other accounts the @c MPToken delta is + * returned. + * + * @param id Account whose share delta is requested. + * @returns The delta, or @c std::nullopt if the entry was not touched. + */ + [[nodiscard]] std::optional + deltaShares(AccountID const& id) const; + + /** + * @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. + */ + [[nodiscard]] static bool + isVaultEmpty(Vault const& vault); + public: + // Compute the coarsest scale required to represent all numbers + [[nodiscard]] static std::int32_t + computeCoarsestScale(std::vector const& numbers); + void visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); - - // Compute the coarsest scale required to represent all numbers - [[nodiscard]] static std::int32_t - computeCoarsestScale(std::vector const& numbers); }; } // namespace xrpl diff --git a/src/libxrpl/tx/invariants/VaultInvariant.cpp b/src/libxrpl/tx/invariants/VaultInvariant.cpp index 2947be3bd4..4aa79279a1 100644 --- a/src/libxrpl/tx/invariants/VaultInvariant.cpp +++ b/src/libxrpl/tx/invariants/VaultInvariant.cpp @@ -186,6 +186,101 @@ ValidVault::visitEntry( } } +std::optional +ValidVault::deltaAssets(AccountID const& id) const +{ + auto const& vaultAsset = afterVault_[0].asset; + auto const lookup = [&](uint256 const& key) -> std::optional { + auto const it = deltas_.find(key); + if (it == deltas_.end()) + return std::nullopt; + return it->second; + }; + + return std::visit( + [&](TIss const& issue) -> std::optional { + if constexpr (std::is_same_v) + { + if (isXRP(issue)) + return lookup(keylet::account(id).key); + auto result = lookup(keylet::line(id, issue).key); + // Trust-line balance is stored from the low-account's perspective; + // negate if id is the high account so the delta is in id's terms. + if (result && id > issue.getIssuer()) + result->delta = -result->delta; + return result; + } + else if constexpr (std::is_same_v) + { + return lookup(keylet::mptoken(issue.getMptID(), id).key); + } + }, + vaultAsset.value()); +} + +std::optional +ValidVault::deltaAssetsTxAccount(STTx const& tx, XRPAmount fee) const +{ + auto const& vaultAsset = afterVault_[0].asset; + auto ret = deltaAssets(tx[sfAccount]); + if (!ret.has_value() || !vaultAsset.native()) + return ret; + + if (auto const delegate = tx[~sfDelegate]; delegate.has_value() && *delegate != tx[sfAccount]) + return ret; + + ret->delta += fee.drops(); + if (ret->delta == kZero) + return std::nullopt; + + return ret; +} + +std::optional +ValidVault::deltaShares(AccountID const& id) const +{ + auto const& afterVault = afterVault_[0]; + auto const it = [&]() { + if (id == afterVault.pseudoId) + return deltas_.find(keylet::mptIssuance(afterVault.shareMPTID).key); + return deltas_.find(keylet::mptoken(afterVault.shareMPTID, id).key); + }(); + + return it != deltas_.end() ? std::optional(it->second) : std::nullopt; +} + +bool +ValidVault::isVaultEmpty(Vault const& vault) +{ + return vault.assetsAvailable == 0 && vault.assetsTotal == 0; +} + +std::int32_t +ValidVault::computeVaultMinScale(DeltaInfo const& vaultDelta, Rules const& rules) const +{ + // Returns the posterior `assetsTotal` scale. + // + // 1. Because STAmounts are normalized, `assetsTotal` (being >= `assetsAvailable`) + // safely represents the coarsest exponent needed for both fields. + // + // 2. The scale may decrease (withdraw/clawback) or increase (deposit). In both cases + // we ensure the vault is in a legitimate state in the post-transaction scale. + auto const& afterVault = afterVault_[0]; + auto const& vaultAsset = afterVault.asset; + if (rules.enabled(fixCleanup3_2_0)) + { + NumberRoundModeGuard const roundGuard(Number::RoundingMode::ToNearest); + return scale(afterVault.assetsTotal, vaultAsset); + } + + auto const& beforeVault = beforeVault_[0]; + auto const totalDelta = + DeltaInfo::makeDelta(beforeVault.assetsTotal, afterVault.assetsTotal, vaultAsset); + auto const availableDelta = + DeltaInfo::makeDelta(beforeVault.assetsAvailable, afterVault.assetsAvailable, vaultAsset); + return computeCoarsestScale({vaultDelta, totalDelta, availableDelta}); +} + bool ValidVault::finalize( STTx const& tx, @@ -445,61 +540,6 @@ ValidVault::finalize( } auto const& vaultAsset = afterVault.asset; - auto const deltaAssets = [&](AccountID const& id) -> std::optional { - auto const get = // - [&](auto const& it, std::int8_t sign = 1) -> std::optional { - if (it == deltas_.end()) - return std::nullopt; - - return DeltaInfo{it->second.delta * sign, it->second.scale}; - }; - - return std::visit( - [&](TIss const& issue) { - if constexpr (std::is_same_v) - { - if (isXRP(issue)) - return get(deltas_.find(keylet::account(id).key)); - return get( - deltas_.find(keylet::line(id, issue).key), id > issue.getIssuer() ? -1 : 1); - } - else if constexpr (std::is_same_v) - { - return get(deltas_.find(keylet::mptoken(issue.getMptID(), id).key)); - } - }, - vaultAsset.value()); - }; - auto const deltaAssetsTxAccount = [&]() -> std::optional { - auto ret = deltaAssets(tx[sfAccount]); - // Nothing returned or not XRP transaction - if (!ret.has_value() || !vaultAsset.native()) - return ret; - - // Delegated transaction; no need to compensate for fees - if (auto const delegate = tx[~sfDelegate]; - delegate.has_value() && *delegate != tx[sfAccount]) - return ret; - - ret->delta += fee.drops(); - if (ret->delta == kZero) - return std::nullopt; - - return ret; - }; - auto const deltaShares = [&](AccountID const& id) -> std::optional { - auto const it = [&]() { - if (id == afterVault.pseudoId) - return deltas_.find(keylet::mptIssuance(afterVault.shareMPTID).key); - return deltas_.find(keylet::mptoken(afterVault.shareMPTID, id).key); - }(); - - return it != deltas_.end() ? std::optional(it->second) : std::nullopt; - }; - - auto const vaultHoldsNoAssets = [&](Vault const& vault) { - return vault.assetsAvailable == 0 && vault.assetsTotal == 0; - }; // Technically this does not need to be a lambda, but it's more // convenient thanks to early "return false"; the not-so-nice @@ -629,16 +669,8 @@ ValidVault::finalize( return false; // That's all we can do } - // Get the coarsest scale to round calculations to - auto const totalDelta = DeltaInfo::makeDelta( - beforeVault.assetsTotal, afterVault.assetsTotal, vaultAsset); - auto const availableDelta = DeltaInfo::makeDelta( - beforeVault.assetsAvailable, afterVault.assetsAvailable, vaultAsset); - auto const minScale = computeCoarsestScale({ - *maybeVaultDeltaAssets, - totalDelta, - availableDelta, - }); + // Get the posterior scale to round calculations to + auto const minScale = computeVaultMinScale(*maybeVaultDeltaAssets, view.rules()); auto const vaultDeltaAssets = roundToAsset(vaultAsset, maybeVaultDeltaAssets->delta, minScale); @@ -669,12 +701,11 @@ ValidVault::finalize( if (!issuerDeposit) { - auto const maybeAccDeltaAssets = deltaAssetsTxAccount(); + auto const maybeAccDeltaAssets = deltaAssetsTxAccount(tx, fee); if (!maybeAccDeltaAssets) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change depositor " - "balance"; + JLOG(j.fatal()) + << "Invariant failed: deposit must change depositor balance"; return false; } auto const localMinScale = @@ -685,19 +716,20 @@ ValidVault::finalize( auto const localVaultDeltaAssets = roundToAsset(vaultAsset, vaultDeltaAssets, localMinScale); + // For IOUs, if the deposit amount is not-representable at depositor trustline + // scale deposit amount could round to zero, giving depositor shares for no + // assets. Unlike withdrawal, we do not allow that. if (accountDeltaAssets >= kZero) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must decrease depositor " - "balance"; + JLOG(j.fatal()) + << "Invariant failed: deposit must decrease depositor balance"; result = false; } if (localVaultDeltaAssets * -1 != accountDeltaAssets) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change vault and " - "depositor balance by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "deposit must change vault and depositor balance by equal amount"; result = false; } } @@ -705,45 +737,38 @@ ValidVault::finalize( if (afterVault.assetsMaximum > kZero && afterVault.assetsTotal > afterVault.assetsMaximum) { - JLOG(j.fatal()) << // - "Invariant failed: deposit assets outstanding must not " - "exceed assets maximum"; + JLOG(j.fatal()) << "Invariant failed: " << // + "deposit assets outstanding must not exceed assets maximum"; result = false; } auto const maybeAccDeltaShares = deltaShares(tx[sfAccount]); if (!maybeAccDeltaShares) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change depositor " - "shares"; + JLOG(j.fatal()) << "Invariant failed: deposit must change depositor shares"; return false; // That's all we can do } - // We don't need to round shares, they are integral MPT + // We don't round shares, they are integral MPT auto const& accountDeltaShares = *maybeAccDeltaShares; if (accountDeltaShares.delta <= kZero) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must increase depositor " - "shares"; + JLOG(j.fatal()) << "Invariant failed: deposit must increase depositor shares"; result = false; } auto const maybeVaultDeltaShares = deltaShares(afterVault.pseudoId); if (!maybeVaultDeltaShares || maybeVaultDeltaShares->delta == kZero) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change vault shares"; + JLOG(j.fatal()) << "Invariant failed: deposit must change vault shares"; return false; // That's all we can do } - // We don't need to round shares, they are integral MPT + // We don't round shares, they are integral MPT auto const& vaultDeltaShares = *maybeVaultDeltaShares; if (vaultDeltaShares.delta * -1 != accountDeltaShares.delta) { - JLOG(j.fatal()) << // - "Invariant failed: deposit must change depositor and " - "vault shares by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "deposit must change depositor and vault shares by equal amount"; result = false; } @@ -751,8 +776,8 @@ ValidVault::finalize( vaultAsset, afterVault.assetsTotal - beforeVault.assetsTotal, minScale); if (assetTotalDelta != vaultDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: deposit and assets " - "outstanding must add up"; + JLOG(j.fatal()) + << "Invariant failed: deposit and assets outstanding must add up"; result = false; } @@ -760,8 +785,7 @@ ValidVault::finalize( vaultAsset, afterVault.assetsAvailable - beforeVault.assetsAvailable, minScale); if (assetAvailableDelta != vaultDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: deposit and assets " - "available must add up"; + JLOG(j.fatal()) << "Invariant failed: deposit and assets available must add up"; result = false; } @@ -772,34 +796,25 @@ ValidVault::finalize( XRPL_ASSERT( !beforeVault_.empty(), - "xrpl::ValidVault::finalize : withdrawal updated a " - "vault"); + "xrpl::ValidVault::finalize : withdrawal updated a vault"); auto const& beforeVault = beforeVault_[0]; auto const maybeVaultDeltaAssets = deltaAssets(afterVault.pseudoId); - if (!maybeVaultDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: withdrawal must " - "change vault balance"; + JLOG(j.fatal()) << "Invariant failed: withdrawal must change vault balance"; return false; // That's all we can do } - // Get the most coarse scale to round calculations to - auto const totalDelta = DeltaInfo::makeDelta( - beforeVault.assetsTotal, afterVault.assetsTotal, vaultAsset); - auto const availableDelta = DeltaInfo::makeDelta( - beforeVault.assetsAvailable, afterVault.assetsAvailable, vaultAsset); - auto const minScale = - computeCoarsestScale({*maybeVaultDeltaAssets, totalDelta, availableDelta}); + // Get the posterior scale to round calculations to + auto const minScale = computeVaultMinScale(*maybeVaultDeltaAssets, view.rules()); auto const vaultPseudoDeltaAssets = roundToAsset(vaultAsset, maybeVaultDeltaAssets->delta, minScale); if (vaultPseudoDeltaAssets >= kZero) { - JLOG(j.fatal()) << "Invariant failed: withdrawal must " - "decrease vault balance"; + JLOG(j.fatal()) << "Invariant failed: withdrawal must decrease vault balance"; result = false; } @@ -814,7 +829,7 @@ ValidVault::finalize( if (!issuerWithdrawal) { - auto const maybeAccDelta = deltaAssetsTxAccount(); + auto const maybeAccDelta = deltaAssetsTxAccount(tx, fee); auto const maybeOtherAccDelta = [&]() -> std::optional { if (auto const destination = tx[~sfDestination]; destination && *destination != tx[sfAccount]) @@ -825,8 +840,7 @@ ValidVault::finalize( if (maybeAccDelta.has_value() == maybeOtherAccDelta.has_value()) { JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change one " - "destination balance"; + "Invariant failed: withdrawal must change one destination balance"; return false; } @@ -835,63 +849,83 @@ ValidVault::finalize( // the scale of destinationDelta can be coarser than // minScale, so we take that into account when rounding - auto const localMinScale = - std::max(minScale, computeCoarsestScale({destinationDelta})); + auto const destinationScale = computeCoarsestScale({destinationDelta}); + auto const localMinScale = std::max(minScale, destinationScale); auto const roundedDestinationDelta = roundToAsset(vaultAsset, destinationDelta.delta, localMinScale); - if (roundedDestinationDelta <= kZero) + // Post-fixCleanup3_2_0: Tolerate zero-rounded destination deltas for IOUs only. + // If the receiver's trust line sits at a coarser scale, the inflow may + // safely round down to zero. + // + // XRP and MPT remain strict. Because they are integer-exact, a zero + // destination delta indicates a true accounting bug, not a rounding artifact. + bool const tolerateZeroDelta = + view.rules().enabled(fixCleanup3_2_0) && !vaultAsset.integral(); + auto const invalidBalanceChange = tolerateZeroDelta + ? roundedDestinationDelta < kZero + : roundedDestinationDelta <= kZero; + if (invalidBalanceChange) { JLOG(j.fatal()) << // - "Invariant failed: withdrawal must increase " - "destination balance"; + "Invariant failed: withdrawal must increase destination balance"; result = false; } auto const localPseudoDeltaAssets = roundToAsset(vaultAsset, vaultPseudoDeltaAssets, localMinScale); - if (localPseudoDeltaAssets * -1 != roundedDestinationDelta) + // For IOU assets near a precision boundary the destination's STAmount + // exponent can shift, making part of the sent value unrepresentable at the + // receiver's new scale — that portion is irreversibly absorbed by the IOU + // rail. Tolerate the mismatch only when the destroyed amount (vault outflow + // minus destination inflow, in Number space) is itself sub-ULP at the + // destination's scale. Floor rounding is used so that values exactly at the + // step boundary are not mistakenly dismissed. Any representable discrepancy + // indicates a real accounting bug and must be caught. + auto const destroyedIsSubUlp = tolerateZeroDelta && + roundToAsset( + vaultAsset, + maybeVaultDeltaAssets->delta * -1 - destinationDelta.delta, + destinationScale, + Number::RoundingMode::Downward) == kZero; + if (!destroyedIsSubUlp && + localPseudoDeltaAssets * -1 != roundedDestinationDelta) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change vault " - "and destination balance by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "withdrawal must change vault and destination balance by equal " + "amount"; result = false; } } - // We don't need to round shares, they are integral MPT + // We don't round shares, they are integral MPT auto const accountDeltaShares = deltaShares(tx[sfAccount]); if (!accountDeltaShares) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change depositor " - "shares"; + JLOG(j.fatal()) << "Invariant failed: withdrawal must change depositor shares"; return false; } if (accountDeltaShares->delta >= kZero) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must decrease depositor " - "shares"; + JLOG(j.fatal()) + << "Invariant failed: withdrawal must decrease depositor shares"; result = false; } - // We don't need to round shares, they are integral MPT + // We don't round shares, they are integral MPT auto const vaultDeltaShares = deltaShares(afterVault.pseudoId); if (!vaultDeltaShares || vaultDeltaShares->delta == kZero) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change vault shares"; + JLOG(j.fatal()) << "Invariant failed: withdrawal must change vault shares"; return false; // That's all we can do } if (vaultDeltaShares->delta * -1 != accountDeltaShares->delta) { - JLOG(j.fatal()) << // - "Invariant failed: withdrawal must change depositor " - "and vault shares by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "withdrawal must change depositor and vault shares by equal amount"; result = false; } @@ -900,8 +934,8 @@ ValidVault::finalize( // Note, vaultBalance is negative (see check above) if (assetTotalDelta != vaultPseudoDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: withdrawal and " - "assets outstanding must add up"; + JLOG(j.fatal()) + << "Invariant failed: withdrawal and assets outstanding must add up"; result = false; } @@ -910,8 +944,8 @@ ValidVault::finalize( if (assetAvailableDelta != vaultPseudoDeltaAssets) { - JLOG(j.fatal()) << "Invariant failed: withdrawal and " - "assets available must add up"; + JLOG(j.fatal()) + << "Invariant failed: withdrawal and assets available must add up"; result = false; } @@ -929,12 +963,11 @@ ValidVault::finalize( // The owner can use clawback to force-burn shares when the // vault is empty but there are outstanding shares if (!(beforeShares && beforeShares->sharesTotal > 0 && - vaultHoldsNoAssets(beforeVault) && beforeVault.owner == tx[sfAccount])) + isVaultEmpty(beforeVault) && beforeVault.owner == tx[sfAccount])) { - JLOG(j.fatal()) << // - "Invariant failed: clawback may only be performed " - "by the asset issuer, or by the vault owner of an " - "empty vault"; + JLOG(j.fatal()) << "Invariant failed: " << // + "clawback may only be performed by the asset issuer, or by the vault " + "owner of an empty vault"; return false; // That's all we can do } } @@ -942,19 +975,13 @@ ValidVault::finalize( auto const maybeVaultDeltaAssets = deltaAssets(afterVault.pseudoId); if (maybeVaultDeltaAssets) { - auto const totalDelta = DeltaInfo::makeDelta( - beforeVault.assetsTotal, afterVault.assetsTotal, vaultAsset); - auto const availableDelta = DeltaInfo::makeDelta( - beforeVault.assetsAvailable, afterVault.assetsAvailable, vaultAsset); auto const minScale = - computeCoarsestScale({*maybeVaultDeltaAssets, totalDelta, availableDelta}); + computeVaultMinScale(*maybeVaultDeltaAssets, view.rules()); auto const vaultDeltaAssets = roundToAsset(vaultAsset, maybeVaultDeltaAssets->delta, minScale); if (vaultDeltaAssets >= kZero) { - JLOG(j.fatal()) << // - "Invariant failed: clawback must decrease vault " - "balance"; + JLOG(j.fatal()) << "Invariant failed: clawback must decrease vault balance"; result = false; } @@ -963,8 +990,7 @@ ValidVault::finalize( if (assetsTotalDelta != vaultDeltaAssets) { JLOG(j.fatal()) << // - "Invariant failed: clawback and assets outstanding " - "must add up"; + "Invariant failed: clawback and assets outstanding must add up"; result = false; } @@ -975,12 +1001,11 @@ ValidVault::finalize( if (assetAvailableDelta != vaultDeltaAssets) { JLOG(j.fatal()) << // - "Invariant failed: clawback and assets available " - "must add up"; + "Invariant failed: clawback and assets available must add up"; result = false; } } - else if (!vaultHoldsNoAssets(beforeVault)) + else if (!isVaultEmpty(beforeVault)) { JLOG(j.fatal()) << // "Invariant failed: clawback must change vault balance"; @@ -998,8 +1023,7 @@ ValidVault::finalize( if (maybeAccountDeltaShares->delta >= kZero) { JLOG(j.fatal()) << // - "Invariant failed: clawback must decrease holder " - "shares"; + "Invariant failed: clawback must decrease holder shares"; result = false; } @@ -1014,9 +1038,8 @@ ValidVault::finalize( if (vaultDeltaShares->delta * -1 != maybeAccountDeltaShares->delta) { - JLOG(j.fatal()) << // - "Invariant failed: clawback must change holder and " - "vault shares by equal amount"; + JLOG(j.fatal()) << "Invariant failed: " << // + "clawback must change holder and vault shares by equal amount"; result = false; } diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index aaaf3cced8..50d165a2ba 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include // IWYU pragma: keep #include @@ -26,6 +28,24 @@ namespace xrpl { +[[nodiscard]] +static STAmount +roundToVaultScale(STAmount const& amount, SLE::const_ref vault) +{ + XRPL_ASSERT(vault && vault->getType() == ltVAULT, "xrpl::roundToVaultScale : valid vault sle"); + XRPL_ASSERT( + amount.asset() == vault->at(sfAsset), "xrpl::roundToVaultScale : valid vault asset"); + + if (amount.integral()) + return amount; + + int const postScale = [&]() { + NumberRoundModeGuard const rg(Number::RoundingMode::ToNearest); + return scale(vault->at(sfAssetsTotal) + amount, vault->at(sfAsset)); + }(); + return roundToScale(amount, postScale, Number::RoundingMode::Downward); +} + NotTEC VaultDeposit::preflight(PreflightContext const& ctx) { @@ -49,9 +69,9 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) return tecNO_ENTRY; auto const& account = ctx.tx[sfAccount]; - auto const assets = ctx.tx[sfAmount]; + auto const amount = ctx.tx[sfAmount]; auto const vaultAsset = vault->at(sfAsset); - if (assets.asset() != vaultAsset) + if (amount.asset() != vaultAsset) return tecWRONG_ASSET; auto const& vaultAccount = vault->at(sfAccount); @@ -63,7 +83,7 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) auto const mptIssuanceID = vault->at(sfShareMPTID); auto const vaultShare = MPTIssue(mptIssuanceID); - if (vaultShare == assets.asset()) + if (vaultShare == amount.asset()) { // LCOV_EXCL_START JLOG(ctx.j.error()) << "VaultDeposit: vault shares and assets cannot be same."; @@ -122,28 +142,69 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) if (auto const ter = requireAuth(ctx.view, vaultAsset, account); !isTesSuccess(ter)) return ter; - if (accountHolds( - ctx.view, - account, - vaultAsset, - FreezeHandling::ZeroIfFrozen, - AuthHandling::ZeroIfUnauthorized, - ctx.j, - SpendableHandling::FullBalance) < assets) + bool const fix320Enabled = ctx.view.rules().enabled(fixCleanup3_2_0); + auto const roundedAmount = fix320Enabled ? roundToVaultScale(amount, vault) : amount; + + if (fix320Enabled && roundedAmount == beast::kZero) + { + JLOG(ctx.j.warn()) << "VaultDeposit: deposit amount: " << ctx.tx[sfAmount] + << " is zero at vault scale"; + return tecPRECISION_LOSS; + } + + auto const accountBalance = accountHolds( + ctx.view, + account, + vaultAsset, + FreezeHandling::ZeroIfFrozen, + AuthHandling::ZeroIfUnauthorized, + ctx.j, + SpendableHandling::FullBalance); + + if (accountBalance < roundedAmount) return tecINSUFFICIENT_FUNDS; + // IOU precision checks + if (fix320Enabled && !roundedAmount.integral()) + { + // reject deposits that would canonicalize to a no-op at the depositor's trustline scale. + // Skipped for issuer-as-depositor: accountHolds returns (kMaxValue @ kMaxOffset) which + // would always trip the predicate. + if (account != amount.getIssuer() && + amount.isZeroAtScale(scale(accountBalance, vaultAsset))) + { + JLOG(ctx.j.warn()) << "VaultDeposit: amount " << amount.getFullText() + << " rounds to zero at counterparty trust-line scale"; + return tecPRECISION_LOSS; + } + } + return tesSUCCESS; } TER VaultDeposit::doApply() { + bool const fix320Enabled = view().rules().enabled(fixCleanup3_2_0); auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID])); if (!vault) return tefINTERNAL; // LCOV_EXCL_LINE auto const vaultAsset = vault->at(sfAsset); - auto const amount = ctx_.tx[sfAmount]; + // Post-amendment IOU only: round Downward to the AssetsTotal precision so + // a sub-ULP tail can't be silently absorbed by one rail and not the other. + auto const amount = + fix320Enabled ? roundToVaultScale(ctx_.tx[sfAmount], vault) : ctx_.tx[sfAmount]; + + // We validated zero-amount in preclaim, if we ended up with zero now, fail hard. + if (amount == beast::kZero) + { + // LCOV_EXCL_START + JLOG(j_.error()) << "VaultDeposit: deposit amount: " << ctx_.tx[sfAmount] << " is zero"; + return tecINTERNAL; + // LCOV_EXCL_STOP + } + // Make sure the depositor can hold shares. auto const mptIssuanceID = (*vault)[sfShareMPTID]; auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); @@ -259,7 +320,7 @@ VaultDeposit::doApply() // trust line into debt the exact case preclaim authorizes via SpendableHandling::FullBalance. // The check thus converts a preclaim- authorized deposit into tefINTERNAL after the asset // transfer. - if (!view().rules().enabled(fixCleanup3_2_0)) + if (!fix320Enabled) { // Sanity check if (accountHolds( diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index c6a9a54a53..d8527876f1 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -6457,6 +6457,489 @@ class Vault_test : public beast::unit_test::Suite runTest(amendments); } + // Bug: DeltaInfo::makeDelta uses max(scale(after), scale(before)) for the + // sfAssetsTotal and sfAssetsAvailable deltas, and visitEntry applies the + // same max() for the vault pseudo-account RippleState. When + // sfAssetsTotal sits exactly at 1e16 (IOU exponent 1, ULP = 10) and a + // withdrawal of 5 USD brings it to 9.999...995e15 (IOU exponent 0, + // ULP = 1), all three computations pick the anterior coarser scale 1. + // roundToAsset(-5, scale=1) collapses to 0, so the invariant check + // vaultPseudoDeltaAssets >= kZero fires even though the state change is + // valid and fully consistent at IOU precision. + // + // Fix (fixCleanup3_2_0): finalize compares the vault pseudo-account and + // sfAssetsTotal/Available deltas directly in Number space, bypassing + // scale-coarsened rounding. + void + testBugMakeDeltaAnteriorScale() + { + using namespace test::jtx; + + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + + env.fund(XRP(100'000), issuer, alice); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + // Trust limit of 2e16, fund exactly 1e16 so deposit lands at the + // IOU scale-1 boundary (exponent 1, ULP = 10). + STAmount const fundAndDeposit{usd.raw(), Number{1, 16}}; + + env(trust(alice, STAmount{usd.raw(), 2, 16})); + env.close(); + env(pay(issuer, alice, fundAndDeposit)); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + // sfAssetsTotal = sfAssetsAvailable = 1e16 (exponent 1, ULP = 10). + env(vault.deposit( + {.depositor = alice, .id = vaultKeylet.key, .amount = fundAndDeposit})); + env.close(); + + // Withdraw 5 USD: -5 is sub-ULP at the anterior scale (ULP = 10) + // but exact at the posterior scale (ULP = 1). The state change is + // consistent; only the invariant's scale selection is wrong. + env(vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = usd(5)}), + Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultWithdraw across IOU scale boundary fires invariant " + "(pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultWithdraw across IOU scale boundary succeeds " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tesSUCCESS); + } + } + + // Bug: DeltaInfo::makeDelta uses max(scale(after), scale(before)) for + // sfAssetsTotal/Available deltas. This is symmetric to + // testBugMakeDeltaAnteriorScale but in the opposite direction: a deposit + // pushes assetsTotal from just below 1e16 (IOU exponent 0, ULP = 1) to just + // above it (exponent 1, ULP = 10). makeDelta picks the coarser *posterior* + // scale 1. The trust line balance rounds from atEdge + 2 = 10,000,000,000,000,001 + // → 1e16, so the pseudo-account delta is only +1 in IOU space. + // roundToAsset(+1, scale=1) = 0 fires "deposit must increase vault balance" + // even though the state change is consistent at every precision boundary. + // + // Fix (fixCleanup3_2_0): computeVaultMinScale uses the posterior Number-space + // scale of sfAssetsTotal (which retains the full value 10,000,000,000,000,001, + // exponent 0), giving minScale = 0. roundToAsset(+1, scale=0) = 1 > 0 and + // the invariant passes. However the transactor's own precision guard fires + // first (bob pays 2 USD, vault receives only 1 due to IOU rounding), so the + // post-amendment result is tecPRECISION_LOSS rather than tesSUCCESS — + // the depositor is protected from silently losing 1 USD to rounding. + void + testBugMakeDeltaPosteriorScale() + { + using namespace test::jtx; + + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(100'000), issuer, alice, bob); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + // atEdge is the largest IOU value with exponent 0 (ULP = 1). + // A deposit of 2 USD brings assetsTotal to 10,000,000,000,000,001 + // in Number space, crossing the 1e16 boundary in IOU space. + STAmount const atEdge{usd.raw(), Number{9'999'999'999'999'999LL}}; + + env(trust(alice, STAmount{usd.raw(), 2, 16})); + env(trust(bob, usd(100))); + env.close(); + env(pay(issuer, alice, atEdge)); + env(pay(issuer, bob, usd(2))); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + // sfAssetsTotal = sfAssetsAvailable = atEdge (exponent 0, ULP = 1) + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = atEdge})); + env.close(); + + // Deposit 2 USD: +2 is sub-ULP at the posterior IOU scale (ULP = 10) + // but exact at the Number scale retained by sfAssetsTotal. + env(vault.deposit({.depositor = bob, .id = vaultKeylet.key, .amount = usd(2)}), + Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultDeposit across IOU scale boundary fires invariant " + "(pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultDeposit across IOU scale boundary succeeds " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tecPRECISION_LOSS); + } + } + + // Bug: ValidVault::visitEntry computes destinationDelta.scale as + // max(before_exponent, after_exponent) for RippleState entries. When a + // withdrawal credits a destination whose IOU balance sits just below a + // power-of-10 boundary (atEdge = 9'999'999'999'999'999), the post-credit + // STAmount rounds up one exponent (exponent 0 → 1), making + // destinationDelta.scale = 1. The invariant then calls + // roundToAsset(+2 USD, scale=1) = 0 and incorrectly fires + // "withdrawal must increase destination balance". + // + // Fix (fixCleanup3_2_0): finalize compares destination delta directly in + // Number space, bypassing scale-coarsened rounding. The transaction + // itself succeeds because the effective IOU credit is non-trivial at + // Number precision even though the STAmount exponent shifted. + void + testVaultWithdrawCanonicalizeToZero() + { + using namespace test::jtx; + + enum class DestKind : bool { ThirdParty = false, Self = true }; + + auto runScenario = [this](FeatureBitset features, DestKind destKind, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(100'000), issuer, alice, bob); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + STAmount const aliceLimit{usd.raw(), 2, 16}; + STAmount const bobLimit{usd.raw(), 2, 16}; + STAmount const atEdge{usd.raw(), Number{9'999'999'999'999'999LL}}; + + env(trust(alice, aliceLimit)); + if (destKind == DestKind::ThirdParty) + env(trust(bob, bobLimit)); + env.close(); + + env(pay(issuer, alice, usd(1'000))); + if (destKind == DestKind::ThirdParty) + env(pay(issuer, bob, atEdge)); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = usd(1'000)})); + env.close(); + + // For the self-destination case, push alice's own trust line to + // the IOU edge so the next withdraw inflow crosses the boundary. + if (destKind == DestKind::Self) + { + env(pay(issuer, alice, atEdge)); + env.close(); + } + + auto tx = vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = usd(2)}); + if (destKind == DestKind::ThirdParty) + tx[sfDestination] = bob.human(); + env(tx, Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultWithdraw to third-party at IOU edge fires invariant " + "(pre-fixCleanup3_2_0)"); + runScenario( + testableAmendments() - fixCleanup3_2_0, DestKind::ThirdParty, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultWithdraw to third-party at IOU edge succeeds " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), DestKind::ThirdParty, tesSUCCESS); + } + { + testcase( + "bug: VaultWithdraw to self at IOU edge fires invariant " + "(pre-fixCleanup3_2_0)"); + runScenario( + testableAmendments() - fixCleanup3_2_0, DestKind::Self, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultWithdraw to self at IOU edge succeeds " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), DestKind::Self, tesSUCCESS); + } + } + + // Bug: the equality check (vault outflow == destination inflow) was + // skipped whenever the destination delta rounded to zero at localMinScale, + // including cases where the vault outflow rounded to a non-zero value and + // a representable amount of value was genuinely destroyed. + // + // Scenario: Bob's IOU balance sits 5 units below the 10^16 STAmount + // precision boundary (atEdge2 = 9,999,999,999,999,995). A withdrawal of + // 6 USD shifts his balance across that boundary: the exponent increments + // (0 → 1), so his effective inflow in Number space is only +5 — 1 USD is + // consumed by the precision-boundary rounding and cannot be credited. + // + // The destroyed amount (1 USD) is sub-ULP at destinationScale=1 (step=10), + // so the check treats it as an unavoidable IOU-precision artefact and + // lets the transaction succeed. + // + // Contrast: if 15 USD were destroyed at the same scale (destroyed ≥ step), + // floor(15/10)=1 ≠ 0 and the invariant would fire — that discrepancy IS + // representable and indicates a real accounting bug. + // + // Pre-fixCleanup3_2_0: the "must increase destination balance" check fires + // because roundedDestinationDelta = 0 ≤ 0. + void + testVaultWithdrawEqualityEnforced() + { + using namespace test::jtx; + + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(100'000), issuer, alice, bob); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + STAmount const aliceLimit{usd.raw(), 2, 16}; + STAmount const bobLimit{usd.raw(), 2, 16}; + // Bob's balance sits 5 units below the 10^16 STAmount precision + // boundary. Receiving 6 USD shifts his exponent 0 → 1; the + // STAmount records +5, not +6 (1 USD is lost to rounding). + STAmount const atEdge2{usd.raw(), Number{9'999'999'999'999'995LL}}; + + env(trust(alice, aliceLimit)); + env(trust(bob, bobLimit)); + env.close(); + + env(pay(issuer, alice, usd(1'000))); + env(pay(issuer, bob, atEdge2)); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = usd(1'000)})); + env.close(); + + // Withdraw 6 USD to Bob: vault loses 6, Bob gains only 5. + // Destroyed amount = 1 USD, which is sub-ULP at destinationScale=1. + auto tx = vault.withdraw({.depositor = alice, .id = vaultKeylet.key, .amount = usd(6)}); + tx[sfDestination] = bob.human(); + env(tx, Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultWithdraw to destination at IOU precision boundary fires " + "invariant (pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultWithdraw to destination at IOU precision boundary succeeds " + "when destroyed amount is sub-ULP (post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tesSUCCESS); + } + } + + // Bug: when a depositor's IOU trustline balance is very large (e.g. + // ~1e17), adding a small deposit (e.g. 1 USD) leaves sfAssetsTotal + // unchanged at IOU precision because the increment is sub-ULP at the + // vault's current asset scale. The vault records the deposit, mints + // shares, and decrements the depositor's trustline, but sfAssetsTotal + // does not change — the conservation invariant fires because the rail + // delta is zero. + // + // Two sub-cases are exercised: + // 1. First-ever deposit into an empty vault: the depositor's own + // trustline has a large balance so 1 USD canonicalizes to zero + // when written back through the IOU rail. + // 2. Subsequent deposit after the vault already holds a large + // sfAssetsTotal: a different depositor (bob, with a small balance) + // sends 1 USD, which again rounds to zero at the vault's coarse + // asset scale. + // + // Fix (fixCleanup3_2_0): the deposit transactor checks whether + // roundToAsset(amount, vault_scale) == 0 and rejects early with + // tecPRECISION_LOSS before any state is modified. + void + testVaultDepositCanonicalizeToZero() + { + using namespace test::jtx; + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + Account const bob{"bob"}; + + env.fund(XRP(100'000), issuer, alice, bob); + env.close(); + + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + + STAmount const trustLimit{usd.raw(), Number{99'999'999'999'999'999LL}}; + STAmount const aliceFund{usd.raw(), Number{99'999'999'999'999'999LL}}; + + env(trust(alice, trustLimit)); + env(trust(bob, trustLimit)); + env.close(); + + env(pay(issuer, alice, aliceFund)); + env(pay(issuer, bob, usd(1000))); + env.close(); + + Vault const vault{env}; + + // Scale=0 so sfAssetsTotal stores whole USD + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + // Alice's deposit canonicalizes to zero at her own trustline scale + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = usd(1)}), + Ter(expected)); + + // Increase vault-scale + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = aliceFund})); + env.close(); + + env(vault.deposit({.depositor = bob, .id = vaultKeylet.key, .amount = usd(1)}), + Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultDeposit below Vault precision canonicalized to zero " + "(pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultDeposit below Vault precision canonicalized to zero " + "(post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tecPRECISION_LOSS); + } + } + + // VaultDeposit by issuer with the vault parked at the IOU 16-digit + // edge (9.999e15). Issuer mints 2 more USD; the vault trust line + // goes 9.999e15 → 10^16, gaining 1 unit instead of 2 (canonicalization). + // + // Pre-fixCleanup3_2_0: the proactive check is absent; the deposit + // applies, then VaultInvariant's "deposit must increase vault + // balance" assertion fires at finalize time on the rounded vault + // delta of zero, returning tecINVARIANT_FAILED. + // Post-amendment: reject deposit that is not representable at Vault scale. + void + testBugIssuerVaultDepositAtEdge() + { + using namespace test::jtx; + + auto runScenario = [this](FeatureBitset features, TER expected) { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const owner{"owner"}; + + env.fund(XRP(100'000), issuer, owner); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + STAmount const trustLimit{usd.raw(), 2, 16}; + STAmount const ownerFund{usd.raw(), Number{9'999'999'999'999'999LL}}; + + env(trust(owner, trustLimit)); + env.close(); + env(pay(issuer, owner, ownerFund)); + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = owner, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + env(vault.deposit({.depositor = owner, .id = vaultKeylet.key, .amount = ownerFund})); + env.close(); + + // Vault pseudo-account is now at 9.999e15. Issuer mints 2 + // more USD. Pre: tecINVARIANT_FAILED at finalize. Post: + // tecPRECISION_LOSS proactively. Either way, no value moves. + env(vault.deposit({.depositor = issuer, .id = vaultKeylet.key, .amount = usd(2)}), + Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultDeposit by issuer at IOU edge fires " + "tecINVARIANT_FAILED at finalize (pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultDeposit by issuer at IOU edge rejects with " + "tecPRECISION_LOSS proactively (post-fixCleanup3_2_0)"); + runScenario(testableAmendments(), tecPRECISION_LOSS); + } + } + void testReferenceHolding() { @@ -6940,6 +7423,12 @@ public: void run() override { + testVaultWithdrawEqualityEnforced(); + testBugIssuerVaultDepositAtEdge(); + testBugMakeDeltaPosteriorScale(); + testBugMakeDeltaAnteriorScale(); + testVaultDepositCanonicalizeToZero(); + testVaultWithdrawCanonicalizeToZero(); testVaultDepositNegativeBalanceFromOppositeLimit(); testSequences(); testPreflight(); diff --git a/src/test/protocol/STAmount_test.cpp b/src/test/protocol/STAmount_test.cpp index c7207589ff..720fafa8f2 100644 --- a/src/test/protocol/STAmount_test.cpp +++ b/src/test/protocol/STAmount_test.cpp @@ -1203,8 +1203,6 @@ public: } } - //-------------------------------------------------------------------------- - void testIsZeroAtScale() { From 49567e728311299dc7a264d30b0245d70d8f6e0a Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 26 May 2026 20:18:40 +0200 Subject: [PATCH 28/87] fix: Fix edge-case where vault-depositor may get stuck (#7139) --- include/xrpl/ledger/helpers/VaultHelpers.h | 35 +- src/libxrpl/ledger/helpers/VaultHelpers.cpp | 37 +- .../tx/transactors/vault/VaultWithdraw.cpp | 89 ++- src/test/app/Vault_test.cpp | 608 ++++++++++++++++++ 4 files changed, 753 insertions(+), 16 deletions(-) diff --git a/include/xrpl/ledger/helpers/VaultHelpers.h b/include/xrpl/ledger/helpers/VaultHelpers.h index 14b0c004cb..29270d913f 100644 --- a/include/xrpl/ledger/helpers/VaultHelpers.h +++ b/include/xrpl/ledger/helpers/VaultHelpers.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include @@ -43,6 +45,14 @@ sharesToAssetsDeposit( /** 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. +*/ +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 @@ -52,6 +62,8 @@ enum class TruncateShares : bool { No = false, Yes = true }; @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. */ @@ -60,7 +72,8 @@ assetsToSharesWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, STAmount const& assets, - TruncateShares truncate = TruncateShares::No); + 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 @@ -69,6 +82,8 @@ assetsToSharesWithdraw( @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. */ @@ -76,6 +91,22 @@ assetsToSharesWithdraw( sharesToAssetsWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, - STAmount const& shares); + 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. +*/ +[[nodiscard]] bool +isSoleShareholder( + ReadView const& view, + AccountID const& account, + std::shared_ptr const& issuance); } // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/VaultHelpers.cpp b/src/libxrpl/ledger/helpers/VaultHelpers.cpp index 8832e0078f..587923953d 100644 --- a/src/libxrpl/ledger/helpers/VaultHelpers.cpp +++ b/src/libxrpl/ledger/helpers/VaultHelpers.cpp @@ -2,11 +2,16 @@ #include #include +#include +#include +#include +#include #include #include #include #include // IWYU pragma: keep +#include #include #include @@ -70,7 +75,8 @@ assetsToSharesWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, STAmount const& assets, - TruncateShares truncate) + TruncateShares truncate, + WaiveUnrealizedLoss waive) { XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesWithdraw : non-negative assets"); XRPL_ASSERT( @@ -80,7 +86,8 @@ assetsToSharesWithdraw( return std::nullopt; // LCOV_EXCL_LINE Number assetTotal = vault->at(sfAssetsTotal); - assetTotal -= vault->at(sfLossUnrealized); + if (waive == WaiveUnrealizedLoss::No) + assetTotal -= vault->at(sfLossUnrealized); STAmount shares{vault->at(sfShareMPTID)}; if (assetTotal == 0) return shares; @@ -96,7 +103,8 @@ assetsToSharesWithdraw( sharesToAssetsWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, - STAmount const& shares) + STAmount const& shares, + WaiveUnrealizedLoss waive) { XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsWithdraw : non-negative shares"); XRPL_ASSERT( @@ -106,7 +114,8 @@ sharesToAssetsWithdraw( return std::nullopt; // LCOV_EXCL_LINE Number assetTotal = vault->at(sfAssetsTotal); - assetTotal -= vault->at(sfLossUnrealized); + if (waive == WaiveUnrealizedLoss::No) + assetTotal -= vault->at(sfLossUnrealized); STAmount assets{vault->at(sfAsset)}; if (assetTotal == 0) return assets; @@ -115,4 +124,24 @@ sharesToAssetsWithdraw( return assets; } +[[nodiscard]] bool +isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref issuance) +{ + XRPL_ASSERT( + issuance && issuance->getType() == ltMPTOKEN_ISSUANCE, + "xrpl::isSoleShareholder : valid issuance SLE"); + + std::uint64_t const outstanding = issuance->at(sfOutstandingAmount); + if (outstanding == 0) + return false; + + auto const shareMPTID = + makeMptID(issuance->getFieldU32(sfSequence), issuance->getAccountID(sfIssuer)); + auto const sleToken = view.read(keylet::mptoken(shareMPTID, account)); + if (!sleToken) + return false; // LCOV_EXCL_LINE + + return sleToken->getFieldU64(sfMPTAmount) == outstanding; +} + } // namespace xrpl diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index ced82f6735..c52d94ac0b 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -4,12 +4,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -26,6 +28,18 @@ namespace xrpl { +static WaiveUnrealizedLoss +shouldWaiveWithdrawal(ReadView const& view, AccountID const& account, SLE::const_ref issuance) +{ + XRPL_ASSERT( + issuance && issuance->getType() == ltMPTOKEN_ISSUANCE, + "xrpl::shouldWaiveWithdrawal : valid issuance sle"); + + return view.rules().enabled(fixCleanup3_2_0) && isSoleShareholder(view, account, issuance) + ? WaiveUnrealizedLoss::Yes + : WaiveUnrealizedLoss::No; +} + NotTEC VaultWithdraw::preflight(PreflightContext const& ctx) { @@ -102,9 +116,14 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx) // LCOV_EXCL_STOP } + // When the user is the sole shareholder they own both the available and future value. + // We waive the unrealized-loss subtraction in this case to avoid user withdrawing all of + // their shares but keeping future value in the vault. + auto const waiveUnrealizedLoss = shouldWaiveWithdrawal(ctx.view, account, sleIssuance); try { - auto const maybeAssets = sharesToAssetsWithdraw(vault, sleIssuance, amount); + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, amount, waiveUnrealizedLoss); if (!maybeAssets) return tefINTERNAL; // LCOV_EXCL_LINE @@ -182,13 +201,19 @@ VaultWithdraw::doApply() MPTIssue const share{mptIssuanceID}; STAmount sharesRedeemed = {share}; STAmount assetsWithdrawn; + + // When the user is the sole shareholder they own both the available and future value. + // We waive the unrealized-loss subtraction in this case to avoid user withdrawing all of their + // shares but keeping future value in the vault. + auto const waiveUnrealizedLoss = shouldWaiveWithdrawal(view(), accountID_, sleIssuance); try { if (amount.asset() == vaultAsset) { // Fixed assets, variable shares. { - auto const maybeShares = assetsToSharesWithdraw(vault, sleIssuance, amount); + auto const maybeShares = assetsToSharesWithdraw( + vault, sleIssuance, amount, TruncateShares::No, waiveUnrealizedLoss); if (!maybeShares) return tecINTERNAL; // LCOV_EXCL_LINE sharesRedeemed = *maybeShares; @@ -196,7 +221,8 @@ VaultWithdraw::doApply() if (sharesRedeemed == beast::kZero) return tecPRECISION_LOSS; - auto const maybeAssets = sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed); + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed, waiveUnrealizedLoss); if (!maybeAssets) return tecINTERNAL; // LCOV_EXCL_LINE assetsWithdrawn = *maybeAssets; @@ -205,7 +231,8 @@ VaultWithdraw::doApply() { // Fixed shares, variable assets. sharesRedeemed = amount; - auto const maybeAssets = sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed); + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed, waiveUnrealizedLoss); if (!maybeAssets) return tecINTERNAL; // LCOV_EXCL_LINE assetsWithdrawn = *maybeAssets; @@ -238,22 +265,64 @@ VaultWithdraw::doApply() auto assetsAvailable = vault->at(sfAssetsAvailable); auto assetsTotal = vault->at(sfAssetsTotal); - [[maybe_unused]] auto const lossUnrealized = vault->at(sfLossUnrealized); + auto const lossUnrealized = vault->at(sfLossUnrealized); XRPL_ASSERT( lossUnrealized <= (assetsTotal - assetsAvailable), "xrpl::VaultWithdraw::doApply : loss and assets do balance"); - // The vault must have enough assets on hand. The vault may hold assets - // that it has already pledged. That is why we look at AssetAvailable - // instead of the pseudo-account balance. + // The vault must have enough assets on hand. if (*assetsAvailable < assetsWithdrawn) { JLOG(j_.debug()) << "VaultWithdraw: vault doesn't hold enough assets"; return tecINSUFFICIENT_FUNDS; } - assetsTotal -= assetsWithdrawn; - assetsAvailable -= assetsWithdrawn; + // Post-fixCleanup3_2_0 "final withdrawal" rule: + // a transaction that would burn every outstanding share is only permitted when the vault is in + // a clean state — no outstanding receivables and no unrealized loss. Otherwise the resulting + // (shares == 0, assetsTotal > 0) state would violate the zero-sized-vault invariant. + // + // When the rule applies, the payout is the remaining sfAssetsAvailable; in a clean vault + // the helper result should already equal that value, and any mismatch is a rounding artifact + // worth logging. + bool const isFinalWithdrawal = + sharesRedeemed == STAmount{share, sleIssuance->at(sfOutstandingAmount)}; + if (view().rules().enabled(fixCleanup3_2_0) && isFinalWithdrawal) + { + // Unreachable: a final withdrawal with lossUnrealized > 0 has + // assetsWithdrawn == assetsTotal > assetsAvailable, which the + // insufficient-funds guard above already rejected. + if (*lossUnrealized != beast::kZero) + { + // LCOV_EXCL_START + UNREACHABLE( + "xrpl::VaultWithdraw::doApply : final withdrawal with non-zero unrealized loss"); + JLOG(j_.fatal()) + << "VaultWithdraw: " // + "Cannot burn all outstanding shares while unrealized loss is non-zero"; + return tefINTERNAL; + // LCOV_EXCL_END + } + + STAmount const allAvailable{vaultAsset, *assetsAvailable}; + if (assetsWithdrawn != allAvailable) + { + JLOG(j_.error()) // + << "VaultWithdraw: final withdrawal share-value mismatch;" + << " computed=" << assetsWithdrawn.getText() + << " assetsAvailable=" << allAvailable.getText(); + } + assetsWithdrawn = allAvailable; + + // Do not let dust accumulate in the Vault. + assetsTotal = 0; + assetsAvailable = 0; + } + else + { + assetsTotal -= assetsWithdrawn; + assetsAvailable -= assetsWithdrawn; + } view().update(vault); auto const& vaultAccount = vault->at(sfAccount); diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index d8527876f1..bf707afae9 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -6457,6 +6457,604 @@ class Vault_test : public beast::unit_test::Suite runTest(amendments); } + // ----------------------------------------------------------------------- + // Helpers and tests: sole-shareholder / stuck-depositor (XLS-0065 + + // fixCleanup3_2_0). The vault-level withdraw behavior is tested here; + // the loan-protocol setup is incidental. + // ----------------------------------------------------------------------- + + FeatureBitset const all_{test::jtx::testableAmendments()}; + std::string const iouCurrency_{"IOU"}; + + // design doc: + // AssetsAvailable ≈ 3,333.50 + // AssetsTotal ≈ 6,666.50 (3,333.50 cash + 3,333 receivable) + // LossUnrealized = 3,333 + // OutstandingShares = sharesLender (5e9 at IOU scale 1e6) + struct StuckDepositorFixture + { + test::jtx::Account issuer{"issuer"}; + test::jtx::Account lender{"lender"}; + test::jtx::Account bob{"bob"}; + test::jtx::Account borrower{"borrower"}; + std::optional asset; + std::optional vaultKeylet; + uint256 brokerID; + std::optional loanKeylet; + MPTID shareAsset; + std::uint64_t sharesLender = 0; + }; + + static constexpr std::int64_t kStuckFunding = 1'000'000; + static constexpr std::int64_t kStuckDepositorIOU = 1'000'000; + static constexpr std::int64_t kStuckBorrowerIOU = 100'000; + static constexpr std::int64_t kStuckDeposit = 5'000; + static constexpr std::int64_t kStuckPrincipal = 3'333; + static constexpr std::uint32_t kStuckPayInterval = 600; + static constexpr std::uint32_t kStuckPayTotal = 2; + + [[nodiscard]] StuckDepositorFixture + setupStuckDepositor(test::jtx::Env& env) + { + using namespace test::jtx; + + StuckDepositorFixture f; + f.asset = f.issuer[iouCurrency_]; + + env.fund(XRP(kStuckFunding), f.issuer, f.lender, f.bob, f.borrower); + env.close(); + + env(trust(f.lender, (*f.asset)(10'000'000))); + env(trust(f.bob, (*f.asset)(10'000'000))); + env(trust(f.borrower, (*f.asset)(10'000'000))); + env.close(); + + env(pay(f.issuer, f.lender, (*f.asset)(kStuckDepositorIOU))); + env(pay(f.issuer, f.bob, (*f.asset)(kStuckDepositorIOU))); + env(pay(f.issuer, f.borrower, (*f.asset)(kStuckBorrowerIOU))); + env.close(); + + // Vault: Lender creates and seeds it; Bob matches the deposit for a + // clean 50/50 split. + Vault const v{env}; + auto [createTx, vaultKeylet] = v.create({.owner = f.lender, .asset = *f.asset}); + env(createTx); + env.close(); + if (!BEAST_EXPECT(env.le(vaultKeylet))) + return f; + f.vaultKeylet = vaultKeylet; + + env(v.deposit({ + .depositor = f.lender, + .id = vaultKeylet.key, + .amount = (*f.asset)(kStuckDeposit), + }), + Ter(tesSUCCESS)); + env(v.deposit({ + .depositor = f.bob, + .id = vaultKeylet.key, + .amount = (*f.asset)(kStuckDeposit), + }), + Ter(tesSUCCESS)); + env.close(); + + // Loan broker: no cover, no management fee, debt cap 10x principal. + f.brokerID = keylet::loanbroker(f.lender.id(), env.seq(f.lender)).key; + { + using namespace loanBroker; + env(set(f.lender, vaultKeylet.key), + kDebtMaximum((*f.asset)(kStuckPrincipal * 10).value())); + env.close(); + } + + // Loan: 3,333 USD principal, impaired immediately. + auto const sleBroker = env.le(keylet::loanbroker(f.brokerID)); + if (!BEAST_EXPECT(sleBroker)) + return f; + f.loanKeylet = keylet::loan(f.brokerID, sleBroker->at(sfLoanSequence)); + + { + using namespace loan; + env(set(f.borrower, f.brokerID, kStuckPrincipal), + Sig(sfCounterpartySignature, f.lender), + kPaymentTotal(kStuckPayTotal), + kPaymentInterval(kStuckPayInterval), + Fee(env.current()->fees().base * 2), + Ter(tesSUCCESS)); + env.close(); + env(manage(f.lender, f.loanKeylet->key, tfLoanImpair), Ter(tesSUCCESS)); + env.close(); + } + + auto const vaultSle = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultSle)) + return f; + BEAST_EXPECT(vaultSle->at(sfLossUnrealized) == (*f.asset)(kStuckPrincipal).value()); + + f.shareAsset = vaultSle->at(sfShareMPTID); + + auto const tokenBob = env.le(keylet::mptoken(f.shareAsset, f.bob.id())); + if (!BEAST_EXPECT(tokenBob)) + return f; + std::uint64_t const sharesBob = tokenBob->getFieldU64(sfMPTAmount); + + // Bob (non-sole) exits at the discounted rate. Always succeeds. + STAmount const bobShareAmt{MPTIssue{f.shareAsset}, Number(sharesBob)}; + env(v.withdraw({ + .depositor = f.bob, + .id = vaultKeylet.key, + .amount = bobShareAmt, + }), + Ter(tesSUCCESS)); + env.close(); + + auto const tokenLender = env.le(keylet::mptoken(f.shareAsset, f.lender.id())); + if (!BEAST_EXPECT(tokenLender)) + return f; + f.sharesLender = tokenLender->getFieldU64(sfMPTAmount); + + auto const sleIssuance = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(sleIssuance)) + return f; + BEAST_EXPECT(sleIssuance->getFieldU64(sfOutstandingAmount) == f.sharesLender); + + auto const vaultAfterBob = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultAfterBob)) + return f; + // After Bob's exit: loss is unchanged (3,333 receivable), and the + // gap between assetsTotal and assetsAvailable equals exactly that + // receivable. + BEAST_EXPECT(vaultAfterBob->at(sfLossUnrealized) == (*f.asset)(kStuckPrincipal).value()); + BEAST_EXPECT( + vaultAfterBob->at(sfAssetsTotal) - vaultAfterBob->at(sfAssetsAvailable) == + vaultAfterBob->at(sfLossUnrealized)); + + return f; + } + + // Reproduces the worked example from the XLS-0065 design doc. The sole + // remaining shareholder asks (via fixed-asset input) for the vault's + // entire AssetsAvailable. Pre-fix this fails with the zero-sized-vault + // invariant violation. Post-fix the full-price exchange rate burns + // only a portion of the shares, the depositor receives all of + // AssetsAvailable, and the residual shares remain backed by the + // impaired-loan receivable. + void + testWithdrawSoleShareholderFixedAssetExit(FeatureBitset features) + { + using namespace test::jtx; + + bool const withFix = features[fixCleanup3_2_0]; + testcase( + std::string{"Vault withdraw: sole shareholder exits via " + "fixed-asset amount with impaired loan"} + + (withFix ? " (fixCleanup3_2_0)" : " (pre-fix)")); + + Env env(*this, features); + auto const f = setupStuckDepositor(env); + if (!f.vaultKeylet || !f.asset || f.sharesLender == 0) + { + BEAST_EXPECT(false); + return; + } + Keylet const& vaultKey = *f.vaultKeylet; + PrettyAsset const& asset = *f.asset; + + auto const vaultBefore = env.le(vaultKey); + if (!BEAST_EXPECT(vaultBefore)) + return; + Number const availableBefore = vaultBefore->at(sfAssetsAvailable); + Number const totalBefore = vaultBefore->at(sfAssetsTotal); + Number const lossBefore = vaultBefore->at(sfLossUnrealized); + + STAmount const lenderBalanceBefore = env.balance(f.lender, asset); + + // The requested amount differs between feature regimes because + // the two regimes are testing different behaviors: + // + // - Pre-fix: request the full AssetsAvailable (3,333.50). Under + // the discounted formula this would burn every outstanding + // share, hitting the zero-sized-vault invariant. The + // transaction is rejected with tecINVARIANT_FAILED — the + // stuck-depositor bug. + // + // - Post-fix: request a strictly smaller amount (1,000 USD). + // The full-price formula burns only ~30% of the outstanding + // shares; the vault retains the rest, backed by the impaired + // receivable. Requesting *exactly* AssetsAvailable post-fix + // would currently fail with tecINSUFFICIENT_FUNDS due to the + // round-to-nearest used by assetsToSharesWithdraw (the + // recomputed payout can overshoot the request by a few ULPs). + // The "force payout to AssetsAvailable" branch in doApply + // only triggers when every share is burned, which is covered + // by the loan-repayment test. + STAmount const requestAssets = + withFix ? asset(1000).value() : STAmount{asset.raw(), availableBefore}; + Vault const v{env}; + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = requestAssets, + }), + Ter(withFix ? TER{tesSUCCESS} : TER{tecINVARIANT_FAILED})); + env.close(); + + auto const vaultAfter = env.le(vaultKey); + if (!BEAST_EXPECT(vaultAfter)) + return; + auto const issuanceAfter = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(issuanceAfter)) + return; + + std::uint64_t const sharesAfter = issuanceAfter->getFieldU64(sfOutstandingAmount); + Number const availableAfter = vaultAfter->at(sfAssetsAvailable); + Number const totalAfter = vaultAfter->at(sfAssetsTotal); + Number const lossAfter = vaultAfter->at(sfLossUnrealized); + + if (!withFix) + { + // Pre-fix: rejected — vault state unchanged. + BEAST_EXPECT(sharesAfter == f.sharesLender); + BEAST_EXPECT(availableAfter == availableBefore); + BEAST_EXPECT(totalAfter == totalBefore); + BEAST_EXPECT(lossAfter == lossBefore); + return; + } + + // Post-fix exact-value derivation (fixture: sharesLender=5e9, + // totalBefore=6666.5, request=1000): + // sharesRedeemed = round(sharesLender * request / totalBefore) + // = round(750,018,750.469) = 750,018,750 + // received = totalBefore * sharesRedeemed / sharesLender + // = 999.999999375 (slightly under 1,000 due to + // integer-share rounding) + constexpr std::uint64_t kExpectedSharesRedeemed = 750'018'750; + Number const expectedReceived = + totalBefore * Number(kExpectedSharesRedeemed) / Number(f.sharesLender); + + BEAST_EXPECT(sharesAfter == f.sharesLender - kExpectedSharesRedeemed); + + // LossUnrealized is unchanged: the loan-protocol side is untouched. + BEAST_EXPECT(lossAfter == lossBefore); + + // The entire (total - available) gap is the impaired receivable, + // i.e. equal to lossUnrealized. + BEAST_EXPECT(totalAfter - availableAfter == lossAfter); + + STAmount const lenderBalanceAfter = env.balance(f.lender, asset); + Number const received{lenderBalanceAfter - lenderBalanceBefore}; + BEAST_EXPECT(received == expectedReceived); + + // Conservation: assets removed from the vault equal what the + // depositor received. + BEAST_EXPECT(totalBefore - totalAfter == received); + BEAST_EXPECT(availableBefore - availableAfter == received); + } + + // Sole shareholder attempts to burn ALL outstanding shares via + // fixed-shares input while the vault still holds an impaired + // receivable. Pre-fix this fails with the zero-sized-vault invariant + // violation. Post-fix the full-price rate causes assetsWithdrawn to + // equal assetsTotal, which exceeds assetsAvailable, so the transaction + // is rejected with tecINSUFFICIENT_FUNDS. + void + testWithdrawSoleShareholderFullSharesRejected(FeatureBitset features) + { + using namespace test::jtx; + + bool const withFix = features[fixCleanup3_2_0]; + testcase( + std::string{"Vault withdraw: sole shareholder full-shares " + "burn is rejected while loss outstanding"} + + (withFix ? " (fixCleanup3_2_0)" : " (pre-fix)")); + + Env env(*this, features); + auto const f = setupStuckDepositor(env); + if (!f.vaultKeylet || f.sharesLender == 0) + { + BEAST_EXPECT(false); + return; + } + Keylet const& vaultKey = *f.vaultKeylet; + + auto const vaultBefore = env.le(vaultKey); + if (!BEAST_EXPECT(vaultBefore)) + return; + Number const availableBefore = vaultBefore->at(sfAssetsAvailable); + Number const totalBefore = vaultBefore->at(sfAssetsTotal); + Number const lossBefore = vaultBefore->at(sfLossUnrealized); + + // Fixed-shares input: ask for ALL outstanding shares. + STAmount const shareAmt{MPTIssue{f.shareAsset}, Number(f.sharesLender)}; + Vault const v{env}; + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = shareAmt, + }), + Ter(withFix ? TER{tecINSUFFICIENT_FUNDS} : TER{tecINVARIANT_FAILED})); + env.close(); + + // Either way the transaction was rejected; vault state unchanged. + auto const vaultAfter = env.le(vaultKey); + if (!BEAST_EXPECT(vaultAfter)) + return; + auto const issuanceAfter = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(issuanceAfter)) + return; + BEAST_EXPECT(issuanceAfter->getFieldU64(sfOutstandingAmount) == f.sharesLender); + BEAST_EXPECT(vaultAfter->at(sfAssetsAvailable) == availableBefore); + BEAST_EXPECT(vaultAfter->at(sfAssetsTotal) == totalBefore); + BEAST_EXPECT(vaultAfter->at(sfLossUnrealized) == lossBefore); + } + + // Post-fix end-to-end resolution: after the sole-shareholder partial + // exit, the loan is repaid in full. With unrealized loss cleared and + // all assets back as cash, the depositor can burn all remaining + // shares and fully exit the vault. The final withdrawal hits the + // "force payout to assetsAvailable" branch in doApply. + void + testWithdrawSoleShareholderLoanRepaymentExit() + { + using namespace test::jtx; + using namespace loan; + + testcase( + "Vault withdraw: sole shareholder fully exits after impaired " + "loan is repaid (fixCleanup3_2_0)"); + + Env env(*this, all_ | fixCleanup3_2_0); + auto const f = setupStuckDepositor(env); + if (!f.vaultKeylet || !f.asset || !f.loanKeylet || f.sharesLender == 0) + { + BEAST_EXPECT(false); + return; + } + Keylet const& vaultKey = *f.vaultKeylet; + Keylet const& loanKey = *f.loanKeylet; + PrettyAsset const& asset = *f.asset; + + Vault const v{env}; + + // Sole-shareholder partial exit (see comment in + // testWithdrawSoleShareholderFixedAssetExit for why we request + // less than full AssetsAvailable). + { + STAmount const requestAssets = asset(1000).value(); + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = requestAssets, + }), + Ter(tesSUCCESS)); + env.close(); + } + + // Confirm the "dormant-but-alive" state from the design doc. The + // partial exit burned exactly 750,018,750 shares (see derivation + // in testWithdrawSoleShareholderFixedAssetExit). + auto const tokenAfterExit = env.le(keylet::mptoken(f.shareAsset, f.lender.id())); + if (!BEAST_EXPECT(tokenAfterExit)) + return; + std::uint64_t const retainedShares = tokenAfterExit->getFieldU64(sfMPTAmount); + BEAST_EXPECT(retainedShares == f.sharesLender - 750'018'750); + + // Borrower repays the loan in full (pays more than the outstanding + // total; the loan transactor caps the receivable). + env(pay(f.borrower, loanKey.key, asset(kStuckPrincipal * 2)), Ter(tesSUCCESS)); + env.close(); + + auto const vaultAfterRepay = env.le(vaultKey); + if (!BEAST_EXPECT(vaultAfterRepay)) + return; + // Repayment converts the 3,333 receivable back to cash; assetsTotal + // is unchanged but assetsAvailable jumps by exactly the same amount, + // and lossUnrealized clears to zero. + BEAST_EXPECT(vaultAfterRepay->at(sfLossUnrealized) == beast::kZero); + BEAST_EXPECT(vaultAfterRepay->at(sfAssetsAvailable) == vaultAfterRepay->at(sfAssetsTotal)); + + STAmount const lenderBalanceBeforeFinal = env.balance(f.lender, asset); + Number const availableBeforeFinal = vaultAfterRepay->at(sfAssetsAvailable); + + // Burn all remaining shares — the clean-state preconditions of + // the "final withdrawal" guard are now satisfied. + STAmount const allShares{MPTIssue{f.shareAsset}, Number(retainedShares)}; + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = allShares, + }), + Ter(tesSUCCESS)); + env.close(); + + auto const vaultFinal = env.le(vaultKey); + if (!BEAST_EXPECT(vaultFinal)) + return; + auto const issuanceFinal = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(issuanceFinal)) + return; + + // Zero-sized vault invariant satisfied: 0 shares, 0 assets. + BEAST_EXPECT(issuanceFinal->getFieldU64(sfOutstandingAmount) == 0); + BEAST_EXPECT(vaultFinal->at(sfAssetsTotal) == beast::kZero); + BEAST_EXPECT(vaultFinal->at(sfAssetsAvailable) == beast::kZero); + BEAST_EXPECT(vaultFinal->at(sfLossUnrealized) == beast::kZero); + + // The final payout equals exactly the AssetsAvailable that + // existed before the call (the "force payout" branch). + STAmount const lenderBalanceAfter = env.balance(f.lender, asset); + Number const finalReceived{lenderBalanceAfter - lenderBalanceBeforeFinal}; + BEAST_EXPECT(finalReceived == availableBeforeFinal); + } + + // Clean-state regression: with no impaired loan, a sole shareholder + // burning all their shares fully empties the vault under both the + // pre-fix and post-fix code paths. Confirms the new logic doesn't + // break the existing happy-path close-out. + void + testWithdrawSoleShareholderCleanVaultUnaffected(FeatureBitset features) + { + using namespace test::jtx; + + bool const withFix = features[fixCleanup3_2_0]; + testcase( + std::string{"Vault withdraw: sole shareholder clean-state " + "close-out unchanged"} + + (withFix ? " (fixCleanup3_2_0)" : " (pre-fix)")); + + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const lender{"lender"}; + + env.fund(XRP(kStuckFunding), issuer, lender); + env.close(); + + PrettyAsset const asset = issuer[iouCurrency_]; + env(trust(lender, asset(10'000'000))); + env.close(); + env(pay(issuer, lender, asset(kStuckDepositorIOU))); + env.close(); + + // Sole shareholder of a clean vault — no loan broker needed. + Vault const v{env}; + auto [createTx, vaultKeylet] = v.create({.owner = lender, .asset = asset}); + env(createTx); + env.close(); + + env(v.deposit({ + .depositor = lender, + .id = vaultKeylet.key, + .amount = asset(kStuckDeposit), + }), + Ter(tesSUCCESS)); + env.close(); + + auto const vaultBefore = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultBefore)) + return; + auto const shareAsset = vaultBefore->at(sfShareMPTID); + auto const tokenLender = env.le(keylet::mptoken(shareAsset, lender.id())); + if (!BEAST_EXPECT(tokenLender)) + return; + std::uint64_t const sharesLender = tokenLender->getFieldU64(sfMPTAmount); + + // Sole shareholder, no loans, no loss. Burn everything. + STAmount const allShares{MPTIssue{shareAsset}, Number(sharesLender)}; + env(v.withdraw({ + .depositor = lender, + .id = vaultKeylet.key, + .amount = allShares, + }), + Ter(tesSUCCESS)); + env.close(); + + auto const vaultFinal = env.le(vaultKeylet); + if (!BEAST_EXPECT(vaultFinal)) + return; + auto const issuanceFinal = env.le(keylet::mptIssuance(shareAsset)); + if (!BEAST_EXPECT(issuanceFinal)) + return; + BEAST_EXPECT(issuanceFinal->getFieldU64(sfOutstandingAmount) == 0); + BEAST_EXPECT(vaultFinal->at(sfAssetsTotal) == beast::kZero); + BEAST_EXPECT(vaultFinal->at(sfAssetsAvailable) == beast::kZero); + BEAST_EXPECT(vaultFinal->at(sfLossUnrealized) == beast::kZero); + + // (Pre-fix path takes the regular code path; post-fix path enters + // the new final-withdrawal guard, which forces payout to exactly + // assetsAvailable. Either way the result is identical for a clean + // vault.) + (void)withFix; + } + + // Sole shareholder in an impaired vault redeems a *partial* count of + // shares via fixed-shares input. Pre-fix the discounted formula is + // used; post-fix the full-price formula is used (waiveUnrealizedLoss + // = Yes). The relative payout therefore differs, and post-fix the + // depositor recovers proportionally more of the residual cash for + // the shares burned. In both cases the vault is left in a valid + // (non-empty) state. + void + testWithdrawSoleShareholderPartialFixedSharesUsesFullPrice() + { + using namespace test::jtx; + + testcase( + "Vault withdraw: sole-shareholder partial fixed-shares uses " + "full-price rate (fixCleanup3_2_0)"); + + Env env(*this, all_ | fixCleanup3_2_0); + auto const f = setupStuckDepositor(env); + if (!f.vaultKeylet || !f.asset || f.sharesLender == 0) + { + BEAST_EXPECT(false); + return; + } + Keylet const& vaultKey = *f.vaultKeylet; + PrettyAsset const& asset = *f.asset; + + auto const vaultBefore = env.le(vaultKey); + if (!BEAST_EXPECT(vaultBefore)) + return; + Number const totalBefore = vaultBefore->at(sfAssetsTotal); + Number const availableBefore = vaultBefore->at(sfAssetsAvailable); + Number const lossBefore = vaultBefore->at(sfLossUnrealized); + + // Burn exactly half of the outstanding shares. + std::uint64_t const halfShares = f.sharesLender / 2; + STAmount const halfAmt{MPTIssue{f.shareAsset}, Number(halfShares)}; + + STAmount const lenderBalanceBefore = env.balance(f.lender, asset); + + Vault const v{env}; + env(v.withdraw({ + .depositor = f.lender, + .id = vaultKey.key, + .amount = halfAmt, + }), + Ter(tesSUCCESS)); + env.close(); + + // Expected payout under the full-price formula: + // assets = totalBefore * halfShares / sharesLender + // which (with halfShares == sharesLender/2) is roughly + // totalBefore / 2. + STAmount const lenderBalanceAfter = env.balance(f.lender, asset); + Number const received{lenderBalanceAfter - lenderBalanceBefore}; + Number const expected = totalBefore * Number(halfShares) / Number(f.sharesLender); + BEAST_EXPECT(received == expected); + + // The full-price payout exceeds the discounted formula by exactly + // lossBefore * halfShares / sharesLender — that's the whole point + // of the waive. + Number const discounted = + (totalBefore - lossBefore) * Number(halfShares) / Number(f.sharesLender); + Number const expectedDelta = lossBefore * Number(halfShares) / Number(f.sharesLender); + BEAST_EXPECT(received - discounted == expectedDelta); + + auto const vaultAfter = env.le(vaultKey); + if (!BEAST_EXPECT(vaultAfter)) + return; + auto const issuanceAfter = env.le(keylet::mptIssuance(f.shareAsset)); + if (!BEAST_EXPECT(issuanceAfter)) + return; + + // Vault remains valid: half the shares remain, lossUnrealized + // is untouched, and the entire (total - available) gap is still + // the impaired receivable. + BEAST_EXPECT( + issuanceAfter->getFieldU64(sfOutstandingAmount) == f.sharesLender - halfShares); + BEAST_EXPECT(vaultAfter->at(sfAssetsTotal) == totalBefore - received); + BEAST_EXPECT(vaultAfter->at(sfLossUnrealized) == lossBefore); + BEAST_EXPECT( + vaultAfter->at(sfAssetsTotal) - vaultAfter->at(sfAssetsAvailable) == + vaultAfter->at(sfLossUnrealized)); + + // Conservation: vault delta matches the depositor's gain. + BEAST_EXPECT(totalBefore - vaultAfter->at(sfAssetsTotal) == received); + BEAST_EXPECT(availableBefore - vaultAfter->at(sfAssetsAvailable) == received); + } + // Bug: DeltaInfo::makeDelta uses max(scale(after), scale(before)) for the // sfAssetsTotal and sfAssetsAvailable deltas, and visitEntry applies the // same max() for the vault pseudo-account RippleState. When @@ -7449,6 +8047,16 @@ public: testAssetsMaximum(); testBug6LimitBypassWithShares(); testRemoveEmptyHoldingLockedAmount(); + + testWithdrawSoleShareholderFixedAssetExit(all_ - fixCleanup3_2_0); + testWithdrawSoleShareholderFixedAssetExit(all_); + testWithdrawSoleShareholderFullSharesRejected(all_ - fixCleanup3_2_0); + testWithdrawSoleShareholderFullSharesRejected(all_); + testWithdrawSoleShareholderCleanVaultUnaffected(all_ - fixCleanup3_2_0); + testWithdrawSoleShareholderCleanVaultUnaffected(all_); + testWithdrawSoleShareholderPartialFixedSharesUsesFullPrice(); + testWithdrawSoleShareholderLoanRepaymentExit(); + testReferenceHolding(); testHoldingDeletionBlocked(); } From 23d08128278a7dcd80cdbd718df8c069a8b25586 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 26 May 2026 19:28:23 +0100 Subject: [PATCH 29/87] style: Use shfmt instead of bashate (#7326) --- .github/scripts/rename/binary.sh | 2 +- .github/scripts/rename/cmake.sh | 12 ++-- .github/scripts/rename/config.sh | 2 +- .github/scripts/rename/copyright.sh | 24 ++++---- .github/scripts/rename/definitions.sh | 2 +- .github/scripts/rename/docs.sh | 2 +- .github/scripts/rename/namespace.sh | 2 +- .pre-commit-config.yaml | 20 +++---- bin/git/setup-upstreams.sh | 24 ++++---- bin/git/squash-branches.sh | 14 ++--- bin/git/update-version.sh | 20 +++---- cspell.config.yaml | 1 + docker/check-sanitizers.sh | 14 +++-- package/build_pkg.sh | 81 +++++++++++++++++---------- package/shared/update-xrpld | 4 +- 15 files changed, 127 insertions(+), 97 deletions(-) diff --git a/.github/scripts/rename/binary.sh b/.github/scripts/rename/binary.sh index cdce6db4ba..89d884538c 100755 --- a/.github/scripts/rename/binary.sh +++ b/.github/scripts/rename/binary.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/cmake.sh b/.github/scripts/rename/cmake.sh index 9c91e8f277..28bf777fed 100755 --- a/.github/scripts/rename/cmake.sh +++ b/.github/scripts/rename/cmake.sh @@ -8,12 +8,12 @@ set -e SED_COMMAND=sed HEAD_COMMAND=head if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi SED_COMMAND=gsed - if ! command -v ghead &> /dev/null; then + if ! command -v ghead &>/dev/null; then echo "Error: ghead is not installed. Please install it using 'brew install coreutils'." exit 1 fi @@ -74,10 +74,10 @@ if grep -q '"xrpld"' cmake/XrplCore.cmake; then # The script has been rerun, so just restore the name of the binary. ${SED_COMMAND} -i 's/"xrpld"/"rippled"/' cmake/XrplCore.cmake elif ! grep -q '"rippled"' cmake/XrplCore.cmake; then - ${HEAD_COMMAND} -n -1 cmake/XrplCore.cmake > cmake.tmp - echo ' # For the time being, we will keep the name of the binary as it was.' >> cmake.tmp - echo ' set_target_properties(xrpld PROPERTIES OUTPUT_NAME "rippled")' >> cmake.tmp - tail -1 cmake/XrplCore.cmake >> cmake.tmp + ${HEAD_COMMAND} -n -1 cmake/XrplCore.cmake >cmake.tmp + echo ' # For the time being, we will keep the name of the binary as it was.' >>cmake.tmp + echo ' set_target_properties(xrpld PROPERTIES OUTPUT_NAME "rippled")' >>cmake.tmp + tail -1 cmake/XrplCore.cmake >>cmake.tmp mv cmake.tmp cmake/XrplCore.cmake fi diff --git a/.github/scripts/rename/config.sh b/.github/scripts/rename/config.sh index 81edcc73d6..ac9debb154 100755 --- a/.github/scripts/rename/config.sh +++ b/.github/scripts/rename/config.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/copyright.sh b/.github/scripts/rename/copyright.sh index 9ebdad1e89..09bc5a8926 100755 --- a/.github/scripts/rename/copyright.sh +++ b/.github/scripts/rename/copyright.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi @@ -62,37 +62,37 @@ done # restoring the verbiage that is already present in LICENSE.md. Ensure that if # the script is run multiple times, duplicate notices are not added. if ! grep -q 'Raw Material Software' include/xrpl/beast/core/CurrentThreadName.h; then - echo -e "// Portions of this file are from JUCE (http://www.juce.com).\n// Copyright (c) 2013 - Raw Material Software Ltd.\n// Please visit http://www.juce.com\n\n$(cat include/xrpl/beast/core/CurrentThreadName.h)" > include/xrpl/beast/core/CurrentThreadName.h + echo -e "// Portions of this file are from JUCE (http://www.juce.com).\n// Copyright (c) 2013 - Raw Material Software Ltd.\n// Please visit http://www.juce.com\n\n$(cat include/xrpl/beast/core/CurrentThreadName.h)" >include/xrpl/beast/core/CurrentThreadName.h fi if ! grep -q 'Dev Null' src/test/app/NetworkID_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/NetworkID_test.cpp)" > src/test/app/NetworkID_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/NetworkID_test.cpp)" >src/test/app/NetworkID_test.cpp fi if ! grep -q 'Dev Null' src/test/app/tx/apply_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/tx/apply_test.cpp)" > src/test/app/tx/apply_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/app/tx/apply_test.cpp)" >src/test/app/tx/apply_test.cpp fi if ! grep -q 'Dev Null' src/test/rpc/ManifestRPC_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ManifestRPC_test.cpp)" > src/test/rpc/ManifestRPC_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ManifestRPC_test.cpp)" >src/test/rpc/ManifestRPC_test.cpp fi if ! grep -q 'Dev Null' src/test/rpc/ValidatorInfo_test.cpp; then - echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ValidatorInfo_test.cpp)" > src/test/rpc/ValidatorInfo_test.cpp + echo -e "// Copyright (c) 2020 Dev Null Productions\n\n$(cat src/test/rpc/ValidatorInfo_test.cpp)" >src/test/rpc/ValidatorInfo_test.cpp fi if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/server_info/Manifest.cpp; then - echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/server_info/Manifest.cpp)" > src/xrpld/rpc/handlers/server_info/Manifest.cpp + echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/server_info/Manifest.cpp)" >src/xrpld/rpc/handlers/server_info/Manifest.cpp fi if ! grep -q 'Dev Null' src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp; then - echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp)" > src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp + echo -e "// Copyright (c) 2019 Dev Null Productions\n\n$(cat src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp)" >src/xrpld/rpc/handlers/admin/status/ValidatorInfo.cpp fi if ! grep -q 'Bougalis' include/xrpl/basics/SlabAllocator.h; then - echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/SlabAllocator.h)" > include/xrpl/basics/SlabAllocator.h # cspell: ignore Nikolaos Bougalis nikb + echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/SlabAllocator.h)" >include/xrpl/basics/SlabAllocator.h # cspell: ignore Nikolaos Bougalis nikb fi if ! grep -q 'Bougalis' include/xrpl/basics/spinlock.h; then - echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/spinlock.h)" > include/xrpl/basics/spinlock.h # cspell: ignore Nikolaos Bougalis nikb + echo -e "// Copyright (c) 2022, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/spinlock.h)" >include/xrpl/basics/spinlock.h # cspell: ignore Nikolaos Bougalis nikb fi if ! grep -q 'Bougalis' include/xrpl/basics/tagged_integer.h; then - echo -e "// Copyright (c) 2014, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/tagged_integer.h)" > include/xrpl/basics/tagged_integer.h # cspell: ignore Nikolaos Bougalis nikb + echo -e "// Copyright (c) 2014, Nikolaos D. Bougalis \n\n$(cat include/xrpl/basics/tagged_integer.h)" >include/xrpl/basics/tagged_integer.h # cspell: ignore Nikolaos Bougalis nikb fi if ! grep -q 'Ritchford' include/xrpl/beast/utility/Zero.h; then - echo -e "// Copyright (c) 2014, Tom Ritchford \n\n$(cat include/xrpl/beast/utility/Zero.h)" > include/xrpl/beast/utility/Zero.h # cspell: ignore Ritchford + echo -e "// Copyright (c) 2014, Tom Ritchford \n\n$(cat include/xrpl/beast/utility/Zero.h)" >include/xrpl/beast/utility/Zero.h # cspell: ignore Ritchford fi # Restore newlines and tabs in string literals in the affected file. diff --git a/.github/scripts/rename/definitions.sh b/.github/scripts/rename/definitions.sh index 5e004afe39..daa5d01e80 100755 --- a/.github/scripts/rename/definitions.sh +++ b/.github/scripts/rename/definitions.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/docs.sh b/.github/scripts/rename/docs.sh index 8b7a362405..9f080b06e5 100755 --- a/.github/scripts/rename/docs.sh +++ b/.github/scripts/rename/docs.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.github/scripts/rename/namespace.sh b/.github/scripts/rename/namespace.sh index aba193b0cf..bb186bc8bc 100755 --- a/.github/scripts/rename/namespace.sh +++ b/.github/scripts/rename/namespace.sh @@ -6,7 +6,7 @@ set -e # On MacOS, ensure that GNU sed is installed and available as `gsed`. SED_COMMAND=sed if [[ "${OSTYPE}" == 'darwin'* ]]; then - if ! command -v gsed &> /dev/null; then + if ! command -v gsed &>/dev/null; then echo "Error: gsed is not installed. Please install it using 'brew install gnu-sed'." exit 1 fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1313ad567c..23441c9dde 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,37 +37,37 @@ repos: exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ - repo: https://github.com/pre-commit/mirrors-clang-format - rev: cd481d7b0bfb5c7b3090c21846317f9a8262e891 # frozen: v22.1.0 + rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5 hooks: - id: clang-format args: [--style=file] "types_or": [c++, c, proto] exclude: ^include/xrpl/protocol_autogen/(transactions|ledger_entries)/ - - repo: https://github.com/BlankSpruce/gersemi - rev: 0.26.0 + - repo: https://github.com/BlankSpruce/gersemi-pre-commit + rev: faadd6a9d852369ca94f4d15b2404c967ba8cb01 # frozen: 0.27.6 hooks: - id: gersemi - repo: https://github.com/rbubley/mirrors-prettier - rev: c2bc67fe8f8f549cc489e00ba8b45aa18ee713b1 # frozen: v3.8.1 + rev: 515f543f5718ebfd6ce22e16708bb32c68ff96e1 # frozen: v3.8.3 hooks: - id: prettier args: [--end-of-line=auto] - repo: https://github.com/psf/black-pre-commit-mirror - rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0 + rev: 4160603246a6b365d4a2af661c6d71b0a0f50478 # frozen: 26.5.1 hooks: - id: black - - repo: https://github.com/openstack/bashate - rev: 5798d24d571676fc407e81df574c1ef57b520f23 # frozen: 2.1.1 + - repo: https://github.com/scop/pre-commit-shfmt + rev: 05c1426671b9237fb5e1444dd63aa5731bec0dfb # frozen: v3.13.1-1 hooks: - - id: bashate - args: ["--ignore=E006"] + - id: shfmt + args: [--write, --indent=4, --case-indent=true] - repo: https://github.com/streetsidesoftware/cspell-cli - rev: a42085ade523f591dca134379a595e7859986445 # frozen: v9.7.0 + rev: 4643f154907327ee0a2c7038f0296e0dd77d9776 # frozen: v10.0.0 hooks: - id: cspell # Spell check changed files exclude: | diff --git a/bin/git/setup-upstreams.sh b/bin/git/setup-upstreams.sh index 57c3f935f9..97c84f5507 100755 --- a/bin/git/setup-upstreams.sh +++ b/bin/git/setup-upstreams.sh @@ -1,8 +1,8 @@ #!/bin/bash if [[ $# -ne 1 || "$1" == "--help" || "$1" == "-h" ]]; then - name=$( basename $0 ) - cat <<- USAGE + name=$(basename $0) + cat <<-USAGE Usage: $name Where is the Github username of the upstream repo. e.g. XRPLF @@ -14,7 +14,7 @@ fi shift user="$1" # Get the origin URL. Expect it be an SSH-style URL -origin=$( git remote get-url origin ) +origin=$(git remote get-url origin) if [[ "${origin}" == "" ]]; then echo Invalid origin remote >&2 exit 1 @@ -22,11 +22,11 @@ fi # echo "Origin: ${origin}" # Parse the origin ifs_orig="${IFS}" -IFS=':' read remote originpath <<< "${origin}" +IFS=':' read remote originpath <<<"${origin}" # echo "Remote: ${remote}, Originpath: ${originpath}" -IFS='@' read sshuser server <<< "${remote}" +IFS='@' read sshuser server <<<"${remote}" # echo "SSHUser: ${sshuser}, Server: ${server}" -IFS='/' read originuser repo <<< "${originpath}" +IFS='/' read originuser repo <<<"${originpath}" # echo "Originuser: ${originuser}, Repo: ${repo}" if [[ "${sshuser}" == "" || "${server}" == "" || "${originuser}" == "" || "${repo}" == "" ]]; then echo "Can't parse origin URL: ${origin}" >&2 @@ -35,9 +35,9 @@ fi upstream="https://${server}/${user}/${repo}" upstreampush="${remote}:${user}/${repo}" upstreamgroup="upstream upstream-push" -current=$( git remote get-url upstream 2>/dev/null ) -currentpush=$( git remote get-url upstream-push 2>/dev/null ) -currentgroup=$( git config remotes.upstreams ) +current=$(git remote get-url upstream 2>/dev/null) +currentpush=$(git remote get-url upstream-push 2>/dev/null) +currentgroup=$(git config remotes.upstreams) if [[ "${current}" == "${upstream}" ]]; then echo "Upstream already set up correctly. Skip" elif [[ -n "${current}" && "${current}" != "${upstream}" && "${current}" != "${upstreampush}" ]]; then @@ -45,9 +45,9 @@ elif [[ -n "${current}" && "${current}" != "${upstream}" && "${current}" != "${u else if [[ "${current}" == "${upstreampush}" ]]; then echo "Upstream set to dangerous push URL. Update." - _run git remote rename upstream upstream-push || \ - _run git remote remove upstream - currentpush=$( git remote get-url upstream-push 2>/dev/null ) + _run git remote rename upstream upstream-push || + _run git remote remove upstream + currentpush=$(git remote get-url upstream-push 2>/dev/null) fi _run git remote add upstream "${upstream}" fi diff --git a/bin/git/squash-branches.sh b/bin/git/squash-branches.sh index eb4aefe23c..ba63f9c148 100755 --- a/bin/git/squash-branches.sh +++ b/bin/git/squash-branches.sh @@ -1,8 +1,8 @@ #!/bin/bash if [[ $# -lt 3 || "$1" == "--help" || "$1" = "-h" ]]; then - name=$( basename $0 ) - cat <<- USAGE + name=$(basename $0) + cat <<-USAGE Usage: $name workbranch base/branch user/branch [user/branch [...]] * workbranch will be created locally from base/branch @@ -16,7 +16,7 @@ fi work="$1" shift -branches=( $( echo "${@}" | sed "s/:/\//" ) ) +branches=($(echo "${@}" | sed "s/:/\//")) base="${branches[0]}" unset branches[0] @@ -24,10 +24,10 @@ set -e users=() for b in "${branches[@]}"; do - users+=( $( echo $b | cut -d/ -f1 ) ) + users+=($(echo $b | cut -d/ -f1)) done -users=( $( printf '%s\n' "${users[@]}" | sort -u ) ) +users=($(printf '%s\n' "${users[@]}" | sort -u)) git fetch --multiple upstreams "${users[@]}" git checkout -B "$work" --no-track "$base" @@ -40,7 +40,7 @@ done # Make sure the commits look right git log --show-signature "$base..HEAD" -parts=( $( echo $base | sed "s/\// /" ) ) +parts=($(echo $base | sed "s/\// /")) repo="${parts[0]}" b="${parts[1]}" push=$repo @@ -50,7 +50,7 @@ fi if [[ "$repo" == "upstream" ]]; then repo="upstreams" fi -cat << PUSH +cat </dev/null ) || true +push=$(git rev-parse --abbrev-ref --symbolic-full-name '@{push}' \ + 2>/dev/null) || true if [[ "${push}" != "" ]]; then echo "Warning: ${push} may already exist." fi -build=$( find -name BuildInfo.cpp ) -sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} > version.cpp && \ -diff "${build}" version.cpp && exit 1 || \ -mv -vi version.cpp ${build} +build=$(find -name BuildInfo.cpp) +sed 's/\(^.*versionString =\).*$/\1 "'${version}'"/' ${build} >version.cpp && + diff "${build}" version.cpp && exit 1 || + mv -vi version.cpp ${build} git diff @@ -47,7 +47,7 @@ git commit -S -m "Set version to ${version}" git log --oneline --first-parent ${base}^.. -cat << PUSH +cat <&2; exit 1 ;; + *) + echo "Unsupported arch: $(uname -m)" >&2 + exit 1 + ;; esac declare -A sanitize=( @@ -35,8 +38,11 @@ for compiler in g++ clang++; do echo "=== Run ${name}-${compiler} ===" output=$("$bin" 2>&1) || true echo "$output" - echo "$output" | grep -q "${expect[$name]}" \ - || { echo "expected '${expect[$name]}' from $bin"; exit 1; } + echo "$output" | grep -q "${expect[$name]}" || + { + echo "expected '${expect[$name]}' from $bin" + exit 1 + } rm -f "$bin" done done diff --git a/package/build_pkg.sh b/package/build_pkg.sh index adac2dc169..f2c2c63c12 100755 --- a/package/build_pkg.sh +++ b/package/build_pkg.sh @@ -36,12 +36,35 @@ SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}" while [[ $# -gt 0 ]]; do case "$1" in - --src-dir) need_arg "$@"; SRC_DIR="$2"; shift 2 ;; - --build-dir) need_arg "$@"; BUILD_DIR="$2"; shift 2 ;; - --pkg-version) need_arg "$@"; PKG_VERSION="$2"; shift 2 ;; - --pkg-release) need_arg "$@"; PKG_RELEASE="$2"; shift 2 ;; - --source-date-epoch) need_arg "$@"; SOURCE_DATE_EPOCH="$2"; shift 2 ;; - -h|--help) usage; exit 0 ;; + --src-dir) + need_arg "$@" + SRC_DIR="$2" + shift 2 + ;; + --build-dir) + need_arg "$@" + BUILD_DIR="$2" + shift 2 + ;; + --pkg-version) + need_arg "$@" + PKG_VERSION="$2" + shift 2 + ;; + --pkg-release) + need_arg "$@" + PKG_RELEASE="$2" + shift 2 + ;; + --source-date-epoch) + need_arg "$@" + SOURCE_DATE_EPOCH="$2" + shift 2 + ;; + -h | --help) + usage + exit 0 + ;; *) echo "Unknown argument: $1" >&2 usage >&2 @@ -109,20 +132,20 @@ stage_common() { local dest="$1" mkdir -p "${dest}" - cp "${BUILD_DIR}/xrpld" "${dest}/xrpld" - cp "${SRC_DIR}/cfg/xrpld-example.cfg" "${dest}/xrpld.cfg" - cp "${SRC_DIR}/cfg/validators-example.txt" "${dest}/validators.txt" - cp "${SRC_DIR}/LICENSE.md" "${dest}/LICENSE.md" - cp "${SRC_DIR}/README.md" "${dest}/README.md" + cp "${BUILD_DIR}/xrpld" "${dest}/xrpld" + cp "${SRC_DIR}/cfg/xrpld-example.cfg" "${dest}/xrpld.cfg" + cp "${SRC_DIR}/cfg/validators-example.txt" "${dest}/validators.txt" + cp "${SRC_DIR}/LICENSE.md" "${dest}/LICENSE.md" + cp "${SRC_DIR}/README.md" "${dest}/README.md" - cp "${SHARED}/xrpld.service" "${dest}/xrpld.service" - cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers" - cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles" - cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate" - cp "${SHARED}/update-xrpld" "${dest}/update-xrpld" - cp "${SHARED}/update-xrpld.service" "${dest}/update-xrpld.service" - cp "${SHARED}/update-xrpld.timer" "${dest}/update-xrpld.timer" - cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset" + cp "${SHARED}/xrpld.service" "${dest}/xrpld.service" + cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers" + cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles" + cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate" + cp "${SHARED}/update-xrpld" "${dest}/update-xrpld" + cp "${SHARED}/update-xrpld.service" "${dest}/update-xrpld.service" + cp "${SHARED}/update-xrpld.timer" "${dest}/update-xrpld.timer" + cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset" } build_rpm() { @@ -159,12 +182,12 @@ build_deb() { cp -r "${DEBIAN_DIR}" "${staging}/debian" # Debhelper auto-discovers these only from debian/. - cp "${staging}/xrpld.service" "${staging}/debian/xrpld.service" - cp "${staging}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers" - cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles" - cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate" + cp "${staging}/xrpld.service" "${staging}/debian/xrpld.service" + cp "${staging}/xrpld.sysusers" "${staging}/debian/xrpld.sysusers" + cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles" + cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate" cp "${staging}/update-xrpld.service" "${staging}/debian/xrpld.update-xrpld.service" - cp "${staging}/update-xrpld.timer" "${staging}/debian/xrpld.update-xrpld.timer" + cp "${staging}/update-xrpld.timer" "${staging}/debian/xrpld.update-xrpld.timer" # Debian '~' marks a pre-release; 3.2.0~b1 sorts before 3.2.0. local deb_full_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}-${PKG_RELEASE}" @@ -175,12 +198,12 @@ build_deb() { # b, rc -> unstable (pre-release) local deb_distribution case "${VER_SUFFIX}" in - "") deb_distribution="stable" ;; - b0) deb_distribution="develop" ;; - *) deb_distribution="unstable" ;; + "") deb_distribution="stable" ;; + b0) deb_distribution="develop" ;; + *) deb_distribution="unstable" ;; esac - cat > "${staging}/debian/changelog" <"${staging}/debian/changelog" < Date: Tue, 26 May 2026 19:35:38 +0100 Subject: [PATCH 30/87] chore: Pin Python packages for codegen using uv (#7329) --- cmake/scripts/codegen/requirements.in | 13 +++ cmake/scripts/codegen/requirements.txt | 118 ++++++++++++++++++++++--- 2 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 cmake/scripts/codegen/requirements.in diff --git a/cmake/scripts/codegen/requirements.in b/cmake/scripts/codegen/requirements.in new file mode 100644 index 0000000000..d799fd60fd --- /dev/null +++ b/cmake/scripts/codegen/requirements.in @@ -0,0 +1,13 @@ +# Python dependencies for XRP Ledger code generation scripts +# +# These packages are required to run the code generation scripts that +# parse macro files and generate C++ wrapper classes. + +# C preprocessor for Python - used to preprocess macro files +pcpp>=1.30 + +# Parser combinator library - used to parse the macro DSL +pyparsing>=3.0.0 + +# Template engine - used to generate C++ code from templates +Mako>=1.2.2 diff --git a/cmake/scripts/codegen/requirements.txt b/cmake/scripts/codegen/requirements.txt index d799fd60fd..ff37548c7b 100644 --- a/cmake/scripts/codegen/requirements.txt +++ b/cmake/scripts/codegen/requirements.txt @@ -1,13 +1,105 @@ -# Python dependencies for XRP Ledger code generation scripts -# -# These packages are required to run the code generation scripts that -# parse macro files and generate C++ wrapper classes. - -# C preprocessor for Python - used to preprocess macro files -pcpp>=1.30 - -# Parser combinator library - used to parse the macro DSL -pyparsing>=3.0.0 - -# Template engine - used to generate C++ code from templates -Mako>=1.2.2 +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.in --generate-hashes --output-file requirements.txt +mako==1.3.12 \ + --hash=sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9 \ + --hash=sha256:9f778e93289bd410bb35daadeb4fc66d95a746f0b75777b942088b7fd7af550a + # via -r requirements.in +markupsafe==3.0.3 \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via mako +pcpp==1.30 \ + --hash=sha256:05fe08292b6da57f385001c891a87f40d6aa7f46787b03e8ba326d20a3297c6e \ + --hash=sha256:5af9fbce55f136d7931ae915fae03c34030a3b36c496e72d9636cedc8e2543a1 + # via -r requirements.in +pyparsing==3.3.2 \ + --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \ + --hash=sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc + # via -r requirements.in From 85af406a0ff234f6a571da46faee7551d254b6f6 Mon Sep 17 00:00:00 2001 From: Andrzej Budzanowski Date: Tue, 26 May 2026 21:35:32 +0200 Subject: [PATCH 31/87] fix: Update `clang-tidy` to include `src/tests` directory header check (#7307) --- .clang-tidy | 2 +- src/tests/libxrpl/helpers/Account.cpp | 2 +- src/tests/libxrpl/helpers/Account.h | 10 +++++----- src/tests/libxrpl/helpers/IOU.h | 3 +-- src/tests/libxrpl/helpers/TestFamily.h | 12 +++++------- src/tests/libxrpl/helpers/TestServiceRegistry.h | 10 ++++------ src/tests/libxrpl/helpers/TxTest.cpp | 2 +- src/tests/libxrpl/helpers/TxTest.h | 2 +- src/tests/libxrpl/tx/AccountSet.cpp | 14 +++++++------- 9 files changed, 26 insertions(+), 31 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index ee6bde6eba..b23d7ccbff 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -199,6 +199,6 @@ CheckOptions: readability-identifier-naming.PublicMemberSuffix: "" readability-identifier-naming.GlobalFunctionIgnoredRegexp: "^(to_string|hash_append|tuple_hash)$" -HeaderFilterRegex: '^.*/(test|xrpl|xrpld)/.*\.(h|hpp|ipp)$' +HeaderFilterRegex: '^.*/(tests?|xrpl|xrpld)/.*\.(h|hpp|ipp)$' ExcludeHeaderFilterRegex: '^.*/protocol_autogen/.*\.(h|hpp)$' WarningsAsErrors: "*" diff --git a/src/tests/libxrpl/helpers/Account.cpp b/src/tests/libxrpl/helpers/Account.cpp index 736ae0a24b..4862309c82 100644 --- a/src/tests/libxrpl/helpers/Account.cpp +++ b/src/tests/libxrpl/helpers/Account.cpp @@ -7,7 +7,7 @@ namespace xrpl::test { -Account const Account::master{"masterpassphrase"}; +Account const Account::kMaster{"masterpassphrase"}; Account::Account(std::string_view name, KeyType type) : name_(name) diff --git a/src/tests/libxrpl/helpers/Account.h b/src/tests/libxrpl/helpers/Account.h index 4e2d6e547f..a92497f2c3 100644 --- a/src/tests/libxrpl/helpers/Account.h +++ b/src/tests/libxrpl/helpers/Account.h @@ -26,7 +26,7 @@ public: * This account is created in the genesis ledger with all 100 billion XRP. * It uses the well-known seed "masterpassphrase". */ - static Account const master; + static Account const kMaster; /** * @brief Create an account from a name. @@ -39,28 +39,28 @@ public: explicit Account(std::string_view name, KeyType type = KeyType::Secp256k1); /** @brief Return the human-readable name. */ - std::string const& + [[nodiscard]] std::string const& name() const noexcept { return name_; } /** @brief Return the AccountID. */ - AccountID const& + [[nodiscard]] AccountID const& id() const noexcept { return id_; } /** @brief Return the public key. */ - PublicKey const& + [[nodiscard]] PublicKey const& pk() const noexcept { return keyPair_.first; } /** @brief Return the secret key. */ - SecretKey const& + [[nodiscard]] SecretKey const& sk() const noexcept { return keyPair_.second; diff --git a/src/tests/libxrpl/helpers/IOU.h b/src/tests/libxrpl/helpers/IOU.h index 18bc69cf33..d80f962edf 100644 --- a/src/tests/libxrpl/helpers/IOU.h +++ b/src/tests/libxrpl/helpers/IOU.h @@ -49,8 +49,7 @@ public: * @param currency The Currency object. * @param issuer The account that issues this currency. */ - IOU(Currency currency, Account const& issuer) - : currency_(std::move(currency)), issuer_(issuer.id()) + IOU(Currency currency, Account const& issuer) : currency_(currency), issuer_(issuer.id()) { XRPL_ASSERT(!isXRP(currency_), "IOU: currency code must not resolve to XRP"); } diff --git a/src/tests/libxrpl/helpers/TestFamily.h b/src/tests/libxrpl/helpers/TestFamily.h index 2f69a26faf..dea7a6d4b4 100644 --- a/src/tests/libxrpl/helpers/TestFamily.h +++ b/src/tests/libxrpl/helpers/TestFamily.h @@ -7,8 +7,7 @@ #include -namespace xrpl { -namespace test { +namespace xrpl::test { /** Test implementation of Family for unit tests. @@ -49,7 +48,7 @@ public: return *db_; } - NodeStore::Database const& + [[nodiscard]] NodeStore::Database const& db() const override { return *db_; @@ -95,8 +94,8 @@ public: void reset() override { - fbCache_->reset(); - tnCache_->reset(); + (*fbCache_).reset(); + (*tnCache_).reset(); } /** Access the test clock for time manipulation in tests. */ @@ -107,5 +106,4 @@ public: } }; -} // namespace test -} // namespace xrpl +} // namespace xrpl::test diff --git a/src/tests/libxrpl/helpers/TestServiceRegistry.h b/src/tests/libxrpl/helpers/TestServiceRegistry.h index 7070927842..0536176344 100644 --- a/src/tests/libxrpl/helpers/TestServiceRegistry.h +++ b/src/tests/libxrpl/helpers/TestServiceRegistry.h @@ -16,8 +16,7 @@ #include #include -namespace xrpl { -namespace test { +namespace xrpl::test { /** Logs implementation that creates TestSink instances. */ class TestLogs : public Logs @@ -63,7 +62,7 @@ private: class TestServiceRegistry : public ServiceRegistry { TestLogs logs_{beast::Severity::Warning}; - boost::asio::io_context io_context_; + boost::asio::io_context ioContext_; TestFamily family_{logs_.journal("TestFamily")}; LoadFeeTrack feeTrack_{logs_.journal("LoadFeeTrack")}; TestNetworkIDService networkIDService_; @@ -344,7 +343,7 @@ public: boost::asio::io_context& getIOContext() override { - return io_context_; + return ioContext_; } Logs& @@ -374,5 +373,4 @@ public: } }; -} // namespace test -} // namespace xrpl +} // namespace xrpl::test diff --git a/src/tests/libxrpl/helpers/TxTest.cpp b/src/tests/libxrpl/helpers/TxTest.cpp index 32667ba13d..aeaa805b27 100644 --- a/src/tests/libxrpl/helpers/TxTest.cpp +++ b/src/tests/libxrpl/helpers/TxTest.cpp @@ -123,7 +123,7 @@ void TxTest::createAccount(Account const& account, XRPAmount xrp, uint32_t accountFlags) { auto const paymentTer = - submit(transactions::PaymentBuilder{Account::master, account, xrp}, Account::master).ter; + submit(transactions::PaymentBuilder{Account::kMaster, account, xrp}, Account::kMaster).ter; if (paymentTer != tesSUCCESS) { diff --git a/src/tests/libxrpl/helpers/TxTest.h b/src/tests/libxrpl/helpers/TxTest.h index 0b578c7e7f..1b3ce460a2 100644 --- a/src/tests/libxrpl/helpers/TxTest.h +++ b/src/tests/libxrpl/helpers/TxTest.h @@ -44,7 +44,7 @@ namespace xrpl::test { */ template constexpr XRPAmount -XRP(T xrp) +XRP(T xrp) // NOLINT(readability-identifier-naming) { return XRPAmount{static_cast(xrp) * kDropsPerXrp.drops()}; } diff --git a/src/tests/libxrpl/tx/AccountSet.cpp b/src/tests/libxrpl/tx/AccountSet.cpp index d00df152ae..726e6e9024 100644 --- a/src/tests/libxrpl/tx/AccountSet.cpp +++ b/src/tests/libxrpl/tx/AccountSet.cpp @@ -432,13 +432,13 @@ TEST(AccountSet, TransferRate) // Test data: {rate to set, expected TER, expected stored rate} std::vector const testData = { - {1.0, tesSUCCESS, 1.0}, - {1.1, tesSUCCESS, 1.1}, - {2.0, tesSUCCESS, 2.0}, - {2.1, temBAD_TRANSFER_RATE, 2.0}, // > 2.0 is invalid - {0.0, tesSUCCESS, 1.0}, // 0 clears the rate (default = 1.0) - {2.0, tesSUCCESS, 2.0}, - {0.9, temBAD_TRANSFER_RATE, 2.0}, // < 1.0 is invalid + {.set = 1.0, .code = tesSUCCESS, .get = 1.0}, + {.set = 1.1, .code = tesSUCCESS, .get = 1.1}, + {.set = 2.0, .code = tesSUCCESS, .get = 2.0}, + {.set = 2.1, .code = temBAD_TRANSFER_RATE, .get = 2.0}, // > 2.0 is invalid + {.set = 0.0, .code = tesSUCCESS, .get = 1.0}, // 0 clears; default rate is 1.0 + {.set = 2.0, .code = tesSUCCESS, .get = 2.0}, + {.set = 0.9, .code = temBAD_TRANSFER_RATE, .get = 2.0}, // < 1.0 is invalid }; TxTest env; From 9623e67b761f2ffd191d9abfe60c5acd82c7e1fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 15:35:52 -0400 Subject: [PATCH 32/87] ci: [DEPENDABOT] bump docker/login-action from 4.1.0 to 4.2.0 (#7318) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-nix-image.yml | 2 +- .github/workflows/reusable-build-docker-image.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index a8fb6eec86..e124c8afd7 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -92,7 +92,7 @@ jobs: type=raw,value=latest - name: Login to GitHub Container Registry - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index e631e02368..f919a550ed 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -60,7 +60,7 @@ jobs: - name: Login to GitHub Container Registry if: inputs.push - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: registry: ghcr.io username: ${{ github.repository_owner }} From 7c597865657ccf6ca8b8b89cdbd5a5506fdcddb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 15:36:00 -0400 Subject: [PATCH 33/87] ci: [DEPENDABOT] bump docker/metadata-action from 6.0.0 to 6.1.0 (#7319) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-nix-image.yml | 2 +- .github/workflows/reusable-build-docker-image.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index e124c8afd7..3987b48099 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -84,7 +84,7 @@ jobs: - name: Docker metadata id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 with: images: ${{ env.IMAGE_NAME }} tags: | diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index f919a550ed..9bb7b06186 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -68,7 +68,7 @@ jobs: - name: Docker metadata id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 with: images: ${{ inputs.image_name }} tags: | From 4584b01bde3d80d00a8813d9a14f6101162ba8e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 15:36:13 -0400 Subject: [PATCH 34/87] ci: [DEPENDABOT] bump docker/build-push-action from 7.1.0 to 7.2.0 (#7320) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/reusable-build-docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index 9bb7b06186..5d60dc29fd 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -78,7 +78,7 @@ jobs: suffix=-${{ steps.vars.outputs.arch }},onlatest=true - name: Build and push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: context: . file: ${{ inputs.dockerfile }} From 108a4c8217b5d84dc10dcfce97ec8404fc622883 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 15:36:21 -0400 Subject: [PATCH 35/87] ci: [DEPENDABOT] bump codecov/codecov-action from 6.0.0 to 6.0.1 (#7321) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/reusable-build-test-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index cc927512ea..4f9b926e98 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -324,7 +324,7 @@ jobs: - name: Upload coverage report if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: disable_search: true disable_telem: true From 2a0feca46b4034f215cb5b4dbe068c836031fa97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 19:36:32 +0000 Subject: [PATCH 36/87] ci: [DEPENDABOT] bump docker/setup-buildx-action from 4.0.0 to 4.1.0 (#7322) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-nix-image.yml | 2 +- .github/workflows/reusable-build-docker-image.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index 3987b48099..fde808642c 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -80,7 +80,7 @@ jobs: steps: - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Docker metadata id: meta diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index 5d60dc29fd..5b555b713f 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -56,7 +56,7 @@ jobs: echo "arch=${PLATFORM##*/}" >> $GITHUB_OUTPUT - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to GitHub Container Registry if: inputs.push From 1162371def825beed3888f79b9f253dea165c13d Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 26 May 2026 21:03:04 +0100 Subject: [PATCH 37/87] ci: Only push docker images in XRPLF/rippled (#7330) --- .github/workflows/build-nix-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index fde808642c..edd35132fa 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -61,12 +61,12 @@ jobs: base_image: ${{ matrix.distro.base_image }} platform: ${{ matrix.target.platform }} runner: ${{ matrix.target.runner }} - push: ${{ github.event_name == 'push' }} + push: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} merge: name: Merge ${{ matrix.distro }} manifest needs: build - if: github.event_name == 'push' + if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} runs-on: ubuntu-latest permissions: contents: read From 7da643d8648959f83d5d6a7d5d3a869171dbf1ca Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Wed, 27 May 2026 11:19:20 -0400 Subject: [PATCH 38/87] fix: Fix a rounding error at the `Number::maxRep` cusp (#7051) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com> --- include/xrpl/basics/Number.h | 137 +++++---- include/xrpl/protocol/Rules.h | 13 + include/xrpl/protocol/STAmount.h | 2 +- src/libxrpl/basics/Number.cpp | 351 ++++++++++++++++-------- src/libxrpl/protocol/IOUAmount.cpp | 2 +- src/libxrpl/protocol/Rules.cpp | 62 ++++- src/libxrpl/protocol/STNumber.cpp | 3 +- src/libxrpl/tx/applySteps.cpp | 18 +- src/test/app/AMMExtended_test.cpp | 109 +++----- src/test/app/AMM_test.cpp | 9 - src/test/app/Invariants_test.cpp | 206 +++++++------- src/test/basics/IOUAmount_test.cpp | 3 +- src/test/basics/Number_test.cpp | 86 +++++- src/test/protocol/STNumber_test.cpp | 3 +- src/test/rpc/GetAggregatePrice_test.cpp | 3 +- 15 files changed, 646 insertions(+), 361 deletions(-) diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index e67f1f534d..3aeb4b5bcd 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include namespace xrpl { @@ -44,11 +46,11 @@ isPowerOfTen(T value) * * min is a power of 10, and * * max = min * 10 - 1. * - * The mantissa_scale enum indicates whether the range is "small" or "large". - * This intentionally restricts the number of MantissaRanges that can be - * instantiated to two: one for each scale. + * The MantissaScale enum indicates properties of the range: size, and some behavioral + * options. This intentionally restricts the number of unique MantissaRanges that can + * be instantiated: one for each scale. * - * The "small" scale is based on the behavior of STAmount for IOUs. It has a min + * The "Small" scale is based on the behavior of STAmount for IOUs. It has a min * value of 10^15, and a max value of 10^16-1. This was sufficient for * uses before Lending Protocol was implemented, mostly related to AMM. * @@ -59,29 +61,54 @@ isPowerOfTen(T value) * STNumber field type, and for internal calculations. That necessitated the * "large" scale. * - * The "large" scale is intended to represent all values that can be represented + * The "Large" scales are intended to represent all values that can be represented * by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max - * value of 10^19-1. + * value of 10^19-1. "LargeLegacy" is like "Large", but preserves + * a rounding error when a computation results in a mantissa of + * Number::kMaxRep that needs to be rounded up, but rounds down + * instead. It will maintain consistent behavior until the fixCleanup3_2_0 + * amendment is enabled. * * Note that if the mentioned amendments are eventually retired, this class - * should be left in place, but the "small" scale option should be removed. This + * should be left in place, but the "Small" scale option should be removed. This * will allow for future expansion beyond 64-bits if it is ever needed. */ -struct MantissaRange +struct MantissaRange final { using rep = std::uint64_t; - enum class MantissaScale { Small, Large }; + enum class MantissaScale { + Small, + // LargeLegacy can be removed when fixCleanup3_2_0 is retired + LargeLegacy, + Large, + }; + + // This entire enum can be removed when fixCleanup3_2_0 is retired + enum class CuspRoundingFix : bool { + Disabled = false, + Enabled = true, + }; explicit constexpr MantissaRange(MantissaScale scale) - : min(getMin(scale)), log(logTen(min).value_or(-1)), scale(scale) + : min(getMin(scale)) + , cuspRoundingFixEnabled(isCuspFixEnabled(scale)) + , log(logTen(min).value_or(-1)) + , scale(scale) { } rep min; rep max{(min * 10) - 1}; + CuspRoundingFix cuspRoundingFixEnabled; int log; MantissaScale scale; + static MantissaRange const& + getMantissaRange(MantissaScale scale); + + static std::set const& + getAllScales(); + private: static constexpr rep getMin(MantissaScale scale) @@ -90,15 +117,35 @@ private: { case MantissaScale::Small: return 1'000'000'000'000'000ULL; + case MantissaScale::LargeLegacy: case MantissaScale::Large: return 1'000'000'000'000'000'000ULL; default: - // Since this can never be called outside a non-constexpr - // context, this throw assures that the build fails if an + // 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"); + throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE } } + + static constexpr CuspRoundingFix + isCuspFixEnabled(MantissaScale scale) + { + switch (scale) + { + case MantissaScale::Small: + case MantissaScale::LargeLegacy: + return CuspRoundingFix::Disabled; + case MantissaScale::Large: + return CuspRoundingFix::Enabled; + 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. @@ -203,7 +250,7 @@ concept Integral64 = std::is_same_v || std::is_same_v + template < + auto MinMantissa, + auto MaxMantissa, + Integral64 T = std::decay_t, + Integral64 TMax = std::decay_t> [[nodiscard]] std::pair - normalizeToRange(T minMantissa, T maxMantissa) const; + normalizeToRange() const; private: static thread_local RoundingMode mode; // The available ranges for mantissa - static constexpr MantissaRange kSmallRange{MantissaRange::MantissaScale::Small}; - static_assert(isPowerOfTen(kSmallRange.min)); - static_assert(kSmallRange.min == 1'000'000'000'000'000LL); - static_assert(kSmallRange.max == 9'999'999'999'999'999LL); - static_assert(kSmallRange.log == 15); - static_assert(kSmallRange.min < kMaxRep); - static_assert(kSmallRange.max < kMaxRep); - static constexpr MantissaRange kLargeRange{MantissaRange::MantissaScale::Large}; - static_assert(isPowerOfTen(kLargeRange.min)); - static_assert(kLargeRange.min == 1'000'000'000'000'000'000ULL); - static_assert(kLargeRange.max == internalrep(9'999'999'999'999'999'999ULL)); - static_assert(kLargeRange.log == 18); - static_assert(kLargeRange.min < kMaxRep); - static_assert(kLargeRange.max > kMaxRep); - // The range for the mantissa when normalized. // Use reference_wrapper to avoid making copies, and prevent accidentally // changing the values inside the range. static thread_local std::reference_wrapper kRange; void - normalize(); + normalize(MantissaRange const& range); /** Normalize Number components to an arbitrary range. * @@ -481,7 +508,8 @@ private: T& mantissa, int& exponent, internalrep const& minMantissa, - internalrep const& maxMantissa); + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled); template friend void @@ -490,7 +518,8 @@ private: T& mantissa, int& exponent, MantissaRange::rep const& minMantissa, - MantissaRange::rep const& maxMantissa); + MantissaRange::rep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled); [[nodiscard]] bool isnormal() const noexcept; @@ -526,7 +555,7 @@ static constexpr Number kNumZero{}; inline Number::Number(bool negative, internalrep mantissa, int exponent, Normalized) : Number(negative, mantissa, exponent, Unchecked{}) { - normalize(); + normalize(kRange); } inline Number::Number(internalrep mantissa, int exponent, Normalized) @@ -696,10 +725,19 @@ Number::isnormal() const noexcept kMinExponent <= exponent_ && exponent_ <= kMaxExponent); } -template +template std::pair -Number::normalizeToRange(T minMantissa, T maxMantissa) const +Number::normalizeToRange() const { + static_assert(std::is_same_v || std::is_same_v); + static_assert(std::is_same_v); + auto constexpr kMIN = static_cast(MinMantissa); + auto constexpr kMAX = static_cast(MaxMantissa); + static_assert(kMIN > 0); + static_assert(kMIN % 10 == 0); + static_assert(kMAX % 10 == 9); + static_assert((kMAX + 1) / 10 == kMIN); + bool negative = negative_; internalrep mantissa = mantissa_; int exponent = exponent_; @@ -711,7 +749,10 @@ Number::normalizeToRange(T minMantissa, T maxMantissa) const "xrpl::Number::normalizeToRange", "Number is non-negative for unsigned range."); } - Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa); + // Don't need to worry about the cuspRounding fix because rounding up will never take the + // mantissa over maxMantissa with a ones digit value other than 0. 0 can safely be truncated. + Number::normalize( + negative, mantissa, exponent, kMIN, kMAX, MantissaRange::CuspRoundingFix::Disabled); auto const sign = negative ? -1 : 1; return std::make_pair(static_cast(sign * mantissa), exponent); @@ -763,6 +804,8 @@ to_string(MantissaRange::MantissaScale const& scale) { case MantissaRange::MantissaScale::Small: return "small"; + case MantissaRange::MantissaScale::LargeLegacy: + return "largeLegacy"; case MantissaRange::MantissaScale::Large: return "large"; default: diff --git a/include/xrpl/protocol/Rules.h b/include/xrpl/protocol/Rules.h index fbbd3d8805..9c17ff2391 100644 --- a/include/xrpl/protocol/Rules.h +++ b/include/xrpl/protocol/Rules.h @@ -122,4 +122,17 @@ private: std::optional saved_; }; +class NumberSO; +class NumberMantissaScaleGuard; + +bool +useRulesGuards(Rules const& rules); + +void +createGuards( + Rules const& rules, + std::optional& stNumberSO, + std::optional& rulesGuard, + std::optional& mantissaScaleGuard); + } // namespace xrpl diff --git a/include/xrpl/protocol/STAmount.h b/include/xrpl/protocol/STAmount.h index a4fffad40c..1a5b442d8b 100644 --- a/include/xrpl/protocol/STAmount.h +++ b/include/xrpl/protocol/STAmount.h @@ -559,7 +559,7 @@ STAmount::fromNumber(A const& a, Number const& number) return STAmount{asset, intValue, 0, negative}; } - auto const [mantissa, exponent] = working.normalizeToRange(kMinValue, kMaxValue); + auto const [mantissa, exponent] = working.normalizeToRange(); return STAmount{asset, mantissa, exponent, negative}; } diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 06bd78d8b0..11f5934b04 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -10,9 +10,11 @@ #include #include #include +#include #include #include #include +#include #include #ifdef _MSC_VER @@ -28,7 +30,76 @@ using int128_t = __int128_t; namespace xrpl { thread_local Number::RoundingMode Number::mode = Number::RoundingMode::ToNearest; -thread_local std::reference_wrapper Number::kRange = kLargeRange; +thread_local std::reference_wrapper Number::kRange = + MantissaRange::getMantissaRange(MantissaRange::MantissaScale::Large); + +std::set const& +MantissaRange::getAllScales() +{ + static std::set const kScales = { + MantissaRange::MantissaScale::Small, + MantissaRange::MantissaScale::LargeLegacy, + MantissaRange::MantissaScale::Large, + }; + return kScales; +} + +std::unordered_map const& +MantissaRange::getRanges() +{ + 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; +} + +MantissaRange const& +MantissaRange::getMantissaRange(MantissaScale scale) +{ + return getRanges().at(scale); +} Number::RoundingMode Number::getround() @@ -51,10 +122,37 @@ Number::getMantissaScale() void Number::setMantissaScale(MantissaRange::MantissaScale scale) { - if (scale != MantissaRange::MantissaScale::Small && - scale != MantissaRange::MantissaScale::Large) + if (!MantissaRange::getAllScales().contains(scale)) logicError("Unknown mantissa scale"); - kRange = scale == MantissaRange::MantissaScale::Small ? kSmallRange : kLargeRange; + kRange = MantissaRange::getMantissaRange(scale); +} + +// Optimization equivalent to: +// auto r = static_cast(u % 10); +// u /= 10; +// return r; +// Derived from Hacker's Delight Second Edition Chapter 10 +// by Henry S. Warren, Jr. +static inline unsigned +divu10(uint128_t& u) +{ + // q = u * 0.75 + auto q = (u >> 1) + (u >> 2); + // iterate towards q = u * 0.8 + q += q >> 4; + q += q >> 8; + q += q >> 16; + q += q >> 32; + q += q >> 64; + // q /= 8 approximately == u / 10 + q >>= 3; + // r = u - q * 10 approximately == u % 10 + auto r = static_cast(u - ((q << 3) + (q << 1))); + // correction c is 1 if r >= 10 else 0 + auto c = (r + 6) >> 4; + u = q + c; + r -= c * 10; + return r; } // Guard @@ -92,6 +190,18 @@ public: unsigned pop() noexcept; + /** 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; + */ + template + void + doDropDigit(T& mantissa, int& exponent) noexcept; + // 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. @@ -107,6 +217,7 @@ public: int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, std::string location); // Modify the result to the correctly rounded value @@ -168,6 +279,27 @@ Number::Guard::pop() noexcept return d; } +template +void +Number::Guard::doDropDigit(T& mantissa, int& exponent) noexcept +{ + push(mantissa % 10); + mantissa /= 10; + ++exponent; +} + +// Use the divu10 optimization for uint128s +template <> +void +Number::Guard::doDropDigit(uint128_t& mantissa, int& exponent) noexcept +{ + // The following is optimization for: + // push(static_cast(mantissa % 10)); + // mantissa /= 10; + push(divu10(mantissa)); + ++exponent; +} + // Returns: // -1 if Guard is less than half // 0 if Guard is exactly half @@ -242,18 +374,60 @@ Number::Guard::doRoundUp( int& exponent, internalrep const& minMantissa, internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, std::string location) { auto r = round(); if (r == 1 || (r == 0 && (mantissa & 1) == 1)) { - ++mantissa; - // Ensure mantissa after incrementing fits within both the - // min/maxMantissa range and is a valid "rep". - if (mantissa > maxMantissa || mantissa > kMaxRep) + auto const safeToIncrement = [&maxMantissa](auto const& mantissa) { + return mantissa < maxMantissa && mantissa < kMaxRep; + }; + if (cuspRoundingFixEnabled == MantissaRange::CuspRoundingFix::Enabled) { - mantissa /= 10; - ++exponent; + // Ensure mantissa after incrementing fits within both the + // min/maxMantissa range and is a valid "rep". + if (safeToIncrement(mantissa)) + { + // Nothing unusual here, just increment the mantissa + ++mantissa; + } + else + { + // Incrementing the mantissa will require dividing, which will require rounding. So + // _don't_ increment the mantissa. Instead, divide and round recursively. It should + // be impossible to recurse more than once, because once the mantissa is divided by + // 10, it will be _well_ under maxMantissa and kMaxRep, so adding 1 will have no + // change of bringing it back over. + doDropDigit(mantissa, exponent); + XRPL_ASSERT_PARTS( + safeToIncrement(mantissa), + "xrpl::Number::Guard::doRoundUp", + "can't recurse more than once"); + doRoundUp( + negative, + mantissa, + exponent, + minMantissa, + maxMantissa, + cuspRoundingFixEnabled, + location); + return; + } + } + else + { + // Need to preserve the incorrect behavior until the fix amendment can be retired, + // because otherwise would risk an unplanned ledger fork. + ++mantissa; + // Ensure mantissa after incrementing fits within both the + // min/maxMantissa range and is a valid "rep". + if (mantissa > maxMantissa || mantissa > kMaxRep) + { + // Don't use doDropDigit here + mantissa /= 10; + ++exponent; + } } } bringIntoRange(negative, mantissa, exponent, minMantissa); @@ -293,9 +467,9 @@ Number::Guard::doRound(rep& drops, std::string location) const { static_assert(sizeof(internalrep) == sizeof(rep)); // This should be impossible, because it's impossible to represent - // "maxRep + 0.6" in Number, regardless of the scale. There aren't - // enough digits available. You'd either get a mantissa of "maxRep" - // or "(maxRep + 1) / 10", neither of which will round up when + // "kMaxRep + 0.6" in Number, regardless of the scale. There aren't + // enough digits available. You'd either get a mantissa of "kMaxRep" + // or "(kMaxRep + 1) / 10", neither of which will round up when // converting to rep, though the latter might overflow _before_ // rounding. Throw(std::string(location)); // LCOV_EXCL_LINE @@ -331,29 +505,11 @@ Number::externalToInternal(rep mantissa) return static_cast(-temp); } -constexpr Number -Number::oneSmall() -{ - return Number{false, Number::kSmallRange.min, -Number::kSmallRange.log, Number::Unchecked{}}; -}; - -constexpr Number kOneSml = Number::oneSmall(); - -constexpr Number -Number::oneLarge() -{ - return Number{false, Number::kLargeRange.min, -Number::kLargeRange.log, Number::Unchecked{}}; -}; - -constexpr Number kOneLrg = Number::oneLarge(); - Number Number::one() { - if (&kRange.get() == &kSmallRange) - return kOneSml; - XRPL_ASSERT(&kRange.get() == &kLargeRange, "Number::one() : valid range"); - return kOneLrg; + auto const& range = kRange.get(); + return Number{false, range.min, -range.log, Number::Unchecked{}}; } // Use the member names in this static function for now so the diff is cleaner @@ -365,7 +521,8 @@ doNormalize( T& mantissa, int& exponent, MantissaRange::rep const& minMantissa, - MantissaRange::rep const& maxMantissa) + MantissaRange::rep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { static constexpr auto kMinExponent = Number::kMinExponent; static constexpr auto kMaxExponent = Number::kMaxExponent; @@ -394,9 +551,7 @@ doNormalize( { if (exponent >= kMaxExponent) throw std::overflow_error("Number::normalize 1"); - g.push(m % 10); - m /= 10; - ++exponent; + g.doDropDigit(m, exponent); } if ((exponent < kMinExponent) || (m < minMantissa)) { @@ -407,7 +562,7 @@ doNormalize( } // When using the largeRange, "m" needs fit within an int64, even if - // the final mantissa_ is going to end up larger to fit within the + // the final mantissa is going to end up larger to fit within the // MantissaRange. Cut it down here so that the rounding will be done while // it's smaller. // @@ -415,26 +570,31 @@ doNormalize( // so "m" will be modified to 990,000,000,000,012,345. Then that value // will be rounded to 990,000,000,000,012,345 or // 990,000,000,000,012,346, depending on the rounding mode. Finally, - // mantissa_ will be "m*10" so it fits within the range, and end up as + // mantissa will be "m*10" so it fits within the range, and end up as // 9,900,000,000,000,123,450 or 9,900,000,000,000,123,460. - // mantissa() will return mantissa_ / 10, and exponent() will return - // exponent_ + 1. + // mantissa() will return mantissa / 10, and exponent() will return + // exponent + 1. if (m > kMaxRep) { if (exponent >= kMaxExponent) throw std::overflow_error("Number::normalize 1.5"); - g.push(m % 10); - m /= 10; - ++exponent; + g.doDropDigit(m, exponent); } // Before modification, m should be within the min/max range. After - // modification, it must be less than maxRep. In other words, the original - // value should have been no more than maxRep * 10. - // (maxRep * 10 > maxMantissa) + // modification, it must be less than kMaxRep. In other words, the original + // value should have been no more than kMaxRep * 10. + // (kMaxRep * 10 > maxMantissa) XRPL_ASSERT_PARTS(m <= kMaxRep, "xrpl::doNormalize", "intermediate mantissa fits in int64"); mantissa = m; - g.doRoundUp(negative, mantissa, exponent, minMantissa, maxMantissa, "Number::normalize 2"); + g.doRoundUp( + negative, + mantissa, + exponent, + minMantissa, + maxMantissa, + cuspRoundingFixEnabled, + "Number::normalize 2"); XRPL_ASSERT_PARTS( mantissa >= minMantissa && mantissa <= maxMantissa, "xrpl::doNormalize", @@ -448,9 +608,10 @@ Number::normalize( uint128_t& mantissa, int& exponent, internalrep const& minMantissa, - internalrep const& maxMantissa) + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa); + doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); } template <> @@ -460,9 +621,10 @@ Number::normalize( unsigned long long& mantissa, int& exponent, internalrep const& minMantissa, - internalrep const& maxMantissa) + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa); + doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); } template <> @@ -472,16 +634,16 @@ Number::normalize( unsigned long& mantissa, int& exponent, internalrep const& minMantissa, - internalrep const& maxMantissa) + internalrep const& maxMantissa, + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa); + doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); } void -Number::normalize() +Number::normalize(MantissaRange const& range) { - auto const& range = kRange.get(); - normalize(negative_, mantissa_, exponent_, range.min, range.max); + normalize(negative_, mantissa_, exponent_, range.min, range.max, range.cuspRoundingFixEnabled); } // Copy the number, but set a new exponent. Because the mantissa doesn't change, @@ -542,9 +704,7 @@ Number::operator+=(Number const& y) g.setNegative(); do { - g.push(xm % 10); - xm /= 10; - ++xe; + g.doDropDigit(xm, xe); } while (xe < ye); } else if (xe > ye) @@ -553,26 +713,30 @@ Number::operator+=(Number const& y) g.setNegative(); do { - g.push(ym % 10); - ym /= 10; - ++ye; + 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; if (xn == yn) { xm += ym; if (xm > maxMantissa || xm > kMaxRep) { - g.push(xm % 10); - xm /= 10; - ++xe; + g.doDropDigit(xm, xe); } - g.doRoundUp(xn, xm, xe, minMantissa, maxMantissa, "Number::addition overflow"); + g.doRoundUp( + xn, + xm, + xe, + minMantissa, + maxMantissa, + cuspRoundingFixEnabled, + "Number::addition overflow"); } else { @@ -598,38 +762,10 @@ Number::operator+=(Number const& y) negative_ = xn; mantissa_ = static_cast(xm); exponent_ = xe; - normalize(); + normalize(range); return *this; } -// Optimization equivalent to: -// auto r = static_cast(u % 10); -// u /= 10; -// return r; -// Derived from Hacker's Delight Second Edition Chapter 10 -// by Henry S. Warren, Jr. -static inline unsigned -divu10(uint128_t& u) -{ - // q = u * 0.75 - auto q = (u >> 1) + (u >> 2); - // iterate towards q = u * 0.8 - q += q >> 4; - q += q >> 8; - q += q >> 16; - q += q >> 32; - q += q >> 64; - // q /= 8 approximately == u / 10 - q >>= 3; - // r = u - q * 10 approximately == u % 10 - auto r = static_cast(u - ((q << 3) + (q << 1))); - // correction c is 1 if r >= 10 else 0 - auto c = (r + 6) >> 4; - u = q + c; - r -= c * 10; - return r; -} - Number& Number::operator*=(Number const& y) { @@ -667,15 +803,13 @@ 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; while (zm > maxMantissa || zm > kMaxRep) { - // The following is optimization for: - // g.push(static_cast(zm % 10)); - // zm /= 10; - g.push(divu10(zm)); - ++ze; + g.doDropDigit(zm, ze); } + xm = static_cast(zm); xe = ze; g.doRoundUp( @@ -684,12 +818,13 @@ Number::operator*=(Number const& y) xe, minMantissa, maxMantissa, + cuspRoundingFixEnabled, "Number::multiplication overflow : exponent is " + std::to_string(xe)); negative_ = zn; mantissa_ = xm; exponent_ = xe; - normalize(); + normalize(range); return *this; } @@ -721,6 +856,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; // Shift by 10^17 gives greatest precision while not overflowing // uint128_t or the cast back to int64_t @@ -728,8 +864,6 @@ Number::operator/=(Number const& y) // log(2^128,10) ~ 38.5 // largeRange.log = 18, fits in 10^19 // f can be up to 10^(38-19) = 10^19 safely - static_assert(kSmallRange.log == 15); - static_assert(kLargeRange.log == 18); bool const small = Number::getMantissaScale() == MantissaRange::MantissaScale::Small; uint128_t const f = small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000ULL; XRPL_ASSERT_PARTS(f >= minMantissa * 10, "Number::operator/=", "factor expected size"); @@ -779,7 +913,7 @@ Number::operator/=(Number const& y) ze -= 3; } } - normalize(zn, zm, ze, minMantissa, maxMantissa); + normalize(zn, zm, ze, minMantissa, maxMantissa, cuspRoundingFixEnabled); negative_ = zn; mantissa_ = static_cast(zm); exponent_ = ze; @@ -801,10 +935,9 @@ operator rep() const g.setNegative(); drops = -drops; } - for (; offset < 0; ++offset) + while (offset < 0) { - g.push(drops % 10); - drops /= 10; + g.doDropDigit(drops, offset); } for (; offset > 0; --offset) { @@ -831,7 +964,7 @@ Number::truncate() const noexcept } // We are guaranteed that normalize() will never throw an exception // because exponent is either negative or zero at this point. - ret.normalize(); + ret.normalize(kRange); return ret; } diff --git a/src/libxrpl/protocol/IOUAmount.cpp b/src/libxrpl/protocol/IOUAmount.cpp index d65ba41a01..d214995809 100644 --- a/src/libxrpl/protocol/IOUAmount.cpp +++ b/src/libxrpl/protocol/IOUAmount.cpp @@ -56,7 +56,7 @@ IOUAmount::fromNumber(Number const& number) // to normalize, which calls fromNumber IOUAmount result{}; std::tie(result.mantissa_, result.exponent_) = - number.normalizeToRange(kMinMantissa, kMaxMantissa); + number.normalizeToRange(); return result; } diff --git a/src/libxrpl/protocol/Rules.cpp b/src/libxrpl/protocol/Rules.cpp index 2c971749b6..08a95145eb 100644 --- a/src/libxrpl/protocol/Rules.cpp +++ b/src/libxrpl/protocol/Rules.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -38,15 +39,68 @@ setCurrentTransactionRules(std::optional r) // Make global changes associated with the rules before the value is moved. // Push the appropriate setting, instead of having the class pull every time // the value is needed. That could get expensive fast. - bool const enableLargeNumbers = + + // 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)); - Number::setMantissaScale( - enableLargeNumbers ? MantissaRange::MantissaScale::Large - : MantissaRange::MantissaScale::Small); + 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) + { + if (enableCuspRoundingFix) + { + return MantissaRange::MantissaScale::Large; + } + return MantissaRange::MantissaScale::LargeLegacy; + } + return MantissaRange::MantissaScale::Small; + }(); + Number::setMantissaScale(range); *getCurrentTransactionRulesRef() = std::move(r); } +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. + // + // 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); +} + +void +createGuards( + Rules const& rules, + std::optional& stNumberSO, + std::optional& rulesGuard, + std::optional& mantissaScaleGuard) +{ + if (useRulesGuards(rules)) + { + // raii classes for the current ledger rules. + // fixUniversalNumber predates the rulesGuard and should be replaced. + stNumberSO.emplace(rules.enabled(fixUniversalNumber)); + rulesGuard.emplace(rules); + } + else + { + // Without those features enabled, always use the old number rules. + mantissaScaleGuard.emplace(MantissaRange::MantissaScale::Small); + } +} + class Rules::Impl { private: diff --git a/src/libxrpl/protocol/STNumber.cpp b/src/libxrpl/protocol/STNumber.cpp index aa3e83515c..8ef7b9760f 100644 --- a/src/libxrpl/protocol/STNumber.cpp +++ b/src/libxrpl/protocol/STNumber.cpp @@ -96,7 +96,8 @@ 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::Large, + Number::getMantissaScale() == MantissaRange::MantissaScale::LargeLegacy || + Number::getMantissaScale() == MantissaRange::MantissaScale::Large, "xrpl::STNumber::add", "STNumber only used with large mantissa scale"); #endif diff --git a/src/libxrpl/tx/applySteps.cpp b/src/libxrpl/tx/applySteps.cpp index e0b1af80e2..217fdd717f 100644 --- a/src/libxrpl/tx/applySteps.cpp +++ b/src/libxrpl/tx/applySteps.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -66,26 +65,15 @@ withTxnType(Rules const& rules, TxType txnType, F&& f) // so these need to be more global. // // To prevent unintentional side effects on existing checks, they will be - // set for every operation only once SingleAssetVault (or later - // LendingProtocol) are enabled. + // set for every operation only once at least one of the relevant amendments + // are enabled. // // See also Transactor::operator(). // std::optional stNumberSO; std::optional rulesGuard; std::optional mantissaScaleGuard; - if (rules.enabled(featureSingleAssetVault) || rules.enabled(featureLendingProtocol)) - { - // raii classes for the current ledger rules. - // fixUniversalNumber predates the rulesGuard and should be replaced. - stNumberSO.emplace(rules.enabled(fixUniversalNumber)); - rulesGuard.emplace(rules); - } - else - { - // Without those features enabled, always use the old number rules. - mantissaScaleGuard.emplace(MantissaRange::MantissaScale::Small); - } + createGuards(rules, stNumberSO, rulesGuard, mantissaScaleGuard); switch (txnType) { diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index 18f2d6df2f..a0a7d0fb15 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -65,10 +65,15 @@ namespace xrpl::test { /** * Tests of AMM that use offers too. */ -struct AMMExtended_test : public jtx::AMMTest +class AMMExtended_test : public jtx::AMMTest { // Use small Number mantissas for the life of this test. - NumberMantissaScaleGuard const sg{xrpl::MantissaRange::MantissaScale::Small}; + NumberMantissaScaleGuard const sg_{xrpl::MantissaRange::MantissaScale::Small}; + + // For now, just disable SAV entirely, which locks in the small Number + // mantissas + FeatureBitset const all_{ + testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; private: void @@ -1349,37 +1354,33 @@ private: testOffers() { using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas - FeatureBitset const all{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testRmFundedOffer(all); - testRmFundedOffer(all - fixAMMv1_1 - fixAMMv1_3); - testEnforceNoRipple(all); - testFillModes(all); - testOfferCrossWithXRP(all); - testOfferCrossWithLimitOverride(all); - testCurrencyConversionEntire(all); - testCurrencyConversionInParts(all); - testCrossCurrencyStartXRP(all); - testCrossCurrencyEndXRP(all); - testCrossCurrencyBridged(all); - testOfferFeesConsumeFunds(all); - testOfferCreateThenCross(all); - testSellFlagExceedLimit(all); - testGatewayCrossCurrency(all); - testGatewayCrossCurrency(all - fixAMMv1_1 - fixAMMv1_3); - testBridgedCross(all); - testSellWithFillOrKill(all); - testTransferRateOffer(all); - testSelfIssueOffer(all); - testBadPathAssert(all); - testSellFlagBasic(all); - testDirectToDirectPath(all); - testDirectToDirectPath(all - fixAMMv1_1 - fixAMMv1_3); - testRequireAuth(all); - testMissingAuth(all); + testRmFundedOffer(all_); + testRmFundedOffer(all_ - fixAMMv1_1 - fixAMMv1_3); + testEnforceNoRipple(all_); + testFillModes(all_); + testOfferCrossWithXRP(all_); + testOfferCrossWithLimitOverride(all_); + testCurrencyConversionEntire(all_); + testCurrencyConversionInParts(all_); + testCrossCurrencyStartXRP(all_); + testCrossCurrencyEndXRP(all_); + testCrossCurrencyBridged(all_); + testOfferFeesConsumeFunds(all_); + testOfferCreateThenCross(all_); + testSellFlagExceedLimit(all_); + testGatewayCrossCurrency(all_); + testGatewayCrossCurrency(all_ - fixAMMv1_1 - fixAMMv1_3); + testBridgedCross(all_); + testSellWithFillOrKill(all_); + testTransferRateOffer(all_); + testSelfIssueOffer(all_); + testBadPathAssert(all_); + testSellFlagBasic(all_); + testDirectToDirectPath(all_); + testDirectToDirectPath(all_ - fixAMMv1_1 - fixAMMv1_3); + testRequireAuth(all_); + testMissingAuth(all_); } void @@ -3516,15 +3517,11 @@ private: testFlow() { using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const all{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testFalseDry(all); - testBookStep(all); - testTransferRateNoOwnerFee(all); - testTransferRateNoOwnerFee(all - fixAMMv1_1 - fixAMMv1_3); + testFalseDry(all_); + testBookStep(all_); + testTransferRateNoOwnerFee(all_); + testTransferRateNoOwnerFee(all_ - fixAMMv1_1 - fixAMMv1_3); testLimitQuality(); testXRPPathLoop(); } @@ -3533,34 +3530,22 @@ private: testCrossingLimits() { using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const all{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testStepLimit(all); - testStepLimit(all - fixAMMv1_1 - fixAMMv1_3); + testStepLimit(all_); + testStepLimit(all_ - fixAMMv1_1 - fixAMMv1_3); } void testDeliverMin() { using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const all{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testConvertAllOfAnAsset(all); - testConvertAllOfAnAsset(all - fixAMMv1_1 - fixAMMv1_3); + testConvertAllOfAnAsset(all_); + testConvertAllOfAnAsset(all_ - fixAMMv1_1 - fixAMMv1_3); } void testDepositAuth() { - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const all{ - jtx::testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testPayment(all); + testPayment(all_); testPayIOU(); } @@ -3568,13 +3553,9 @@ private: testFreeze() { using namespace test::jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas in the transaction engine - FeatureBitset const sa{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; - testRippleState(sa); - testGlobalFreeze(sa); - testOffersWhenFrozen(sa); + testRippleState(all_); + testGlobalFreeze(all_); + testOffersWhenFrozen(all_); } void diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index 17f959ae3c..64972c24ab 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -2625,10 +2625,6 @@ private: using namespace jtx; using namespace std::chrono; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas - features = features - featureSingleAssetVault - featureLendingProtocol; - // Auction slot initially is owned by AMM creator, who pays 0 price. // Bid 110 tokens. Pay bidMin. @@ -3337,11 +3333,6 @@ private: testcase("Basic Payment"); using namespace jtx; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas - features = - features - featureSingleAssetVault - featureLendingProtocol - featureLendingProtocol; - // Payment 100USD for 100XRP. // Force one path with tfNoRippleDirect. testAMM( diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index c8a6e813de..432dccce61 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -4767,109 +4767,119 @@ class Invariants_test : public beast::unit_test::Suite std::vector values; }; - NumberMantissaScaleGuard const g{MantissaRange::MantissaScale::Large}; - - auto makeDelta = [&vaultAsset](Number const& n) -> ValidVault::DeltaInfo { - return {.delta = n, .scale = scale(n, vaultAsset.raw())}; - }; - - auto const testCases = std::vector{ - { - .name = "No values", - .expectedMinScale = 0, - .values = {}, - }, - { - .name = "Mixed integer and Number values", - .expectedMinScale = -15, - .values = {makeDelta(1), makeDelta(-1), makeDelta(Number{10, -1})}, - }, - { - .name = "Mixed scales", - .expectedMinScale = -17, - .values = - {makeDelta(Number{1, -2}), makeDelta(Number{5, -3}), makeDelta(Number{3, -2})}, - }, - { - .name = "Equal scales", - .expectedMinScale = -16, - .values = - {makeDelta(Number{1, -1}), makeDelta(Number{5, -1}), makeDelta(Number{1, -1})}, - }, - { - .name = "Mixed mantissa sizes", - .expectedMinScale = -12, - .values = - {makeDelta(Number{1}), - makeDelta(Number{1234, -3}), - makeDelta(Number{12345, -6}), - makeDelta(Number{123, 1})}, - }, - }; - - for (auto const& tc : testCases) + for (auto const mantissaScale : { + MantissaRange::MantissaScale::LargeLegacy, + MantissaRange::MantissaScale::Large, + }) { - testcase("vault computeCoarsestScale: " + tc.name); + NumberMantissaScaleGuard const g{mantissaScale}; - auto const actualScale = ValidVault::computeCoarsestScale(tc.values); + auto makeDelta = [&vaultAsset](Number const& n) -> ValidVault::DeltaInfo { + return {.delta = n, .scale = scale(n, vaultAsset.raw())}; + }; - BEAST_EXPECTS( - actualScale == tc.expectedMinScale, - "expected: " + std::to_string(tc.expectedMinScale) + - ", actual: " + std::to_string(actualScale)); - for (auto const& num : tc.values) - { - // None of these scales are far enough apart that rounding the - // values would lose information, so check that the rounded - // value matches the original. - auto const actualRounded = roundToAsset(vaultAsset, num.delta, actualScale); - BEAST_EXPECTS( - actualRounded == num.delta, - "number " + to_string(num.delta) + " rounded to scale " + - std::to_string(actualScale) + " is " + to_string(actualRounded)); - } - } - - auto const testCases2 = std::vector{ - { - .name = "False equivalence", - .expectedMinScale = -15, - .values = - { - makeDelta(Number{1234567890123456789, -18}), - makeDelta(Number{12345, -4}), - makeDelta(Number{1}), - }, - }, - }; - - // Unlike the first set of test cases, the values in these test could - // look equivalent if using the wrong scale. - for (auto const& tc : testCases2) - { - testcase("vault computeCoarsestScale: " + tc.name); - - auto const actualScale = ValidVault::computeCoarsestScale(tc.values); - - BEAST_EXPECTS( - actualScale == tc.expectedMinScale, - "expected: " + std::to_string(tc.expectedMinScale) + - ", actual: " + std::to_string(actualScale)); - std::optional first; - Number firstRounded; - for (auto const& num : tc.values) - { - if (!first) + auto const testCases = std::vector{ { - first = num.delta; - firstRounded = roundToAsset(vaultAsset, num.delta, actualScale); - continue; - } - auto const numRounded = roundToAsset(vaultAsset, num.delta, actualScale); + .name = "No values", + .expectedMinScale = 0, + .values = {}, + }, + { + .name = "Mixed integer and Number values", + .expectedMinScale = -15, + .values = {makeDelta(1), makeDelta(-1), makeDelta(Number{10, -1})}, + }, + { + .name = "Mixed scales", + .expectedMinScale = -17, + .values = + {makeDelta(Number{1, -2}), + makeDelta(Number{5, -3}), + makeDelta(Number{3, -2})}, + }, + { + .name = "Equal scales", + .expectedMinScale = -16, + .values = + {makeDelta(Number{1, -1}), + makeDelta(Number{5, -1}), + makeDelta(Number{1, -1})}, + }, + { + .name = "Mixed mantissa sizes", + .expectedMinScale = -12, + .values = + {makeDelta(Number{1}), + makeDelta(Number{1234, -3}), + makeDelta(Number{12345, -6}), + makeDelta(Number{123, 1})}, + }, + }; + + for (auto const& tc : testCases) + { + testcase("vault computeCoarsestScale: " + tc.name); + + auto const actualScale = ValidVault::computeCoarsestScale(tc.values); + BEAST_EXPECTS( - numRounded != firstRounded, - "at a scale of " + std::to_string(actualScale) + " " + to_string(num.delta) + - " == " + to_string(*first)); + actualScale == tc.expectedMinScale, + "expected: " + std::to_string(tc.expectedMinScale) + + ", actual: " + std::to_string(actualScale)); + for (auto const& num : tc.values) + { + // None of these scales are far enough apart that rounding the + // values would lose information, so check that the rounded + // value matches the original. + auto const actualRounded = roundToAsset(vaultAsset, num.delta, actualScale); + BEAST_EXPECTS( + actualRounded == num.delta, + "number " + to_string(num.delta) + " rounded to scale " + + std::to_string(actualScale) + " is " + to_string(actualRounded)); + } + } + + auto const testCases2 = std::vector{ + { + .name = "False equivalence", + .expectedMinScale = -15, + .values = + { + makeDelta(Number{1234567890123456789, -18}), + makeDelta(Number{12345, -4}), + makeDelta(Number{1}), + }, + }, + }; + + // Unlike the first set of test cases, the values in these test could + // look equivalent if using the wrong scale. + for (auto const& tc : testCases2) + { + testcase("vault computeCoarsestScale: " + tc.name); + + auto const actualScale = ValidVault::computeCoarsestScale(tc.values); + + BEAST_EXPECTS( + actualScale == tc.expectedMinScale, + "expected: " + std::to_string(tc.expectedMinScale) + + ", actual: " + std::to_string(actualScale)); + std::optional first; + Number firstRounded; + for (auto const& num : tc.values) + { + if (!first) + { + first = num.delta; + firstRounded = roundToAsset(vaultAsset, num.delta, actualScale); + continue; + } + auto const numRounded = roundToAsset(vaultAsset, num.delta, actualScale); + BEAST_EXPECTS( + numRounded != firstRounded, + "at a scale of " + std::to_string(actualScale) + " " + + to_string(num.delta) + " == " + to_string(*first)); + } } } } diff --git a/src/test/basics/IOUAmount_test.cpp b/src/test/basics/IOUAmount_test.cpp index ef053449a5..b652d27625 100644 --- a/src/test/basics/IOUAmount_test.cpp +++ b/src/test/basics/IOUAmount_test.cpp @@ -156,8 +156,7 @@ public: BEAST_EXPECTS(result == expected, ss.str()); }; - for (auto const mantissaSize : - {MantissaRange::MantissaScale::Small, MantissaRange::MantissaScale::Large}) + for (auto const mantissaSize : MantissaRange::getAllScales()) { NumberMantissaScaleGuard const mg(mantissaSize); diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 2a4e176ae5..26060c70e9 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -6,7 +6,10 @@ #include #include +#include + #include +#include #include #include #include @@ -19,6 +22,24 @@ namespace xrpl { class Number_test : public beast::unit_test::Suite { + using BigInt = boost::multiprecision::cpp_int; + + static std::string + fmt(BigInt const& value) + { + auto s = to_string(value); + std::string out; + int count = 0; + for (auto it = s.rbegin(); it != s.rend(); ++it) + { + if (count != 0 && count % 3 == 0 && (isdigit(*it) != 0)) + out.insert(out.begin(), '_'); + out.insert(out.begin(), *it); + ++count; + } + return out; + } + public: void testZero() @@ -178,7 +199,6 @@ public: {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}, Number{6, -1}, Number{Number::kMaxRep / 10, 1}}, {Number{Number::kMaxRep - 1}, Number{1, 0}, Number{Number::kMaxRep}}, // Test extremes { @@ -189,16 +209,22 @@ public: Number{2, 19}, }, { - // Does not round. Mantissas are going to be > maxRep, so if + // Does not round. Mantissas are going to be > kMaxRep, so if // added together as uint64_t's, the result will overflow. // With addition using uint128_t, there's no problem. After // normalizing, the resulting mantissa ends up less than - // maxRep. + // kMaxRep. 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{}}, }, }); + auto const cLargeLegacy = std::to_array({ + {Number{Number::kMaxRep}, Number{6, -1}, Number{Number::kMaxRep / 10, 1}}, + }); + auto const cLargeCorrected = std::to_array({ + {Number{Number::kMaxRep}, Number{6, -1}, Number{(Number::kMaxRep / 10) + 1, 1}}, + }); auto test = [this](auto const& c) { for (auto const& [x, y, z] : c) { @@ -215,6 +241,14 @@ public: else { test(cLarge); + if (scale == MantissaRange::MantissaScale::LargeLegacy) + { + test(cLargeLegacy); + } + else + { + test(cLargeCorrected); + } } { bool caught = false; @@ -835,7 +869,7 @@ public: /* auto tests = [&](auto const& cSmall, auto const& cLarge) { test(cSmall); - if (scale != MantissaRange::mantissa_scale::small) + if (scale != MantissaRange::MantissaScale::Small) test(cLarge); }; */ @@ -1266,6 +1300,7 @@ public: "9223372036854775e3"); } break; + case MantissaRange::MantissaScale::LargeLegacy: case MantissaRange::MantissaScale::Large: // Test the edges // ((exponent < -(28)) || (exponent > -(8))))) @@ -1551,11 +1586,48 @@ public: } } + void + testUpwardRoundsDown() + { + testcase << "upward rounding produces a value below exact at kMaxRep cusp"; + + NumberMantissaScaleGuard const mg{MantissaRange::MantissaScale::Large}; + NumberRoundModeGuard const rg{Number::RoundingMode::Upward}; + + constexpr std::int64_t kAValue = 1'000'000'000'000'049'863LL; + constexpr std::int64_t kBValue = 9'223'372'036'854'315'903LL; + + Number const a = kAValue; + Number const b = kBValue; + Number const product = a * b; + + // Exact reference in BigInt. + 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 signedDifference = storedValue - exactProduct; + + log << "\n" + << " a = " << fmt(BigInt(kAValue)) << "\n" + << " b = " << fmt(BigInt(kBValue)) << "\n" + << " exact a*b = " << fmt(exactProduct) << "\n" + << " stored = " << fmt(storedValue) << "\n" + << " stored - exact = " << fmt(signedDifference) << "\n" + << " upward = " << (signedDifference >= 0 ? "held" : "VIOLATED") << "\n"; + + BEAST_EXPECT(signedDifference >= 0); + BEAST_EXPECT(product.mantissa() == (std::numeric_limits::max() / 10) + 1); + BEAST_EXPECT(product.exponent() == 19); + } + void run() override { - for (auto const scale : - {MantissaRange::MantissaScale::Small, MantissaRange::MantissaScale::Large}) + for (auto const scale : MantissaRange::getAllScales()) { NumberMantissaScaleGuard const sg(scale); testZero(); @@ -1580,6 +1652,8 @@ public: testRounding(); testInt64(); } + // This test sets its own number range + testUpwardRoundsDown(); } }; diff --git a/src/test/protocol/STNumber_test.cpp b/src/test/protocol/STNumber_test.cpp index 4d95bc1ef7..5c9c3fd83c 100644 --- a/src/test/protocol/STNumber_test.cpp +++ b/src/test/protocol/STNumber_test.cpp @@ -280,8 +280,7 @@ struct STNumber_test : public beast::unit_test::Suite { static_assert(!std::is_convertible_v); - for (auto const scale : - {MantissaRange::MantissaScale::Small, MantissaRange::MantissaScale::Large}) + for (auto const scale : MantissaRange::getAllScales()) { NumberMantissaScaleGuard const sg(scale); testcase << to_string(Number::getMantissaScale()); diff --git a/src/test/rpc/GetAggregatePrice_test.cpp b/src/test/rpc/GetAggregatePrice_test.cpp index 1de08da205..37ecc54172 100644 --- a/src/test/rpc/GetAggregatePrice_test.cpp +++ b/src/test/rpc/GetAggregatePrice_test.cpp @@ -177,8 +177,7 @@ public: auto const all = testableAmendments(); for (auto const& feats : {all - featureSingleAssetVault - featureLendingProtocol, all}) { - for (auto const mantissaSize : - {MantissaRange::MantissaScale::Small, MantissaRange::MantissaScale::Large}) + for (auto const mantissaSize : MantissaRange::getAllScales()) { // Regardless of the features enabled, RPC is controlled by // the global mantissa size. And since it's a thread-local, From 1438bf1c6764056216bfeab5027e0eccc12fd870 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 27 May 2026 18:20:57 +0100 Subject: [PATCH 39/87] release: Bump version to 3.2.0-rc1 (#7335) --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 5613a9366e..4e95683308 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -23,7 +23,7 @@ namespace { //------------------------------------------------------------------------------ // clang-format off // NOLINTNEXTLINE(readability-identifier-naming) -char const* const versionString = "3.2.0-b7" +char const* const versionString = "3.2.0-rc1" // clang-format on ; From 396d772a15a634d4a50e3f8e22e7378d1eed914d Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 27 May 2026 15:10:33 -0400 Subject: [PATCH 40/87] refactor: Enable support for `fixCleanup3_2_0` amendment (#7347) --- include/xrpl/protocol/detail/features.macro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index fd62b74d59..c99c1e5ce8 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -15,7 +15,7 @@ // Add new amendments to the top of this list. // Keep it sorted in reverse chronological order. -XRPL_FIX (Cleanup3_2_0, Supported::No, VoteBehavior::DefaultNo) +XRPL_FIX (Cleanup3_2_0, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FEATURE(MPTokensV2, Supported::No, VoteBehavior::DefaultNo) XRPL_FIX (Cleanup3_1_3, Supported::Yes, VoteBehavior::DefaultYes) XRPL_FIX (BatchInnerSigs, Supported::No, VoteBehavior::DefaultNo) From 1acc42313c01e7e114333390b4485e3e176ba66f Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 27 May 2026 15:11:38 -0400 Subject: [PATCH 41/87] release: Bump version to 3.2.0-rc2 (#7348) --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 4e95683308..2e33d03088 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -23,7 +23,7 @@ namespace { //------------------------------------------------------------------------------ // clang-format off // NOLINTNEXTLINE(readability-identifier-naming) -char const* const versionString = "3.2.0-rc1" +char const* const versionString = "3.2.0-rc2" // clang-format on ; From f9551ac5ca08bb851b2a17d2f5259de13c105876 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 27 May 2026 20:24:18 +0100 Subject: [PATCH 42/87] style: Run shfmt on workflows, actions and markdown bash code (#7333) --- .github/actions/build-deps/action.yml | 18 +- .github/actions/generate-version/action.yml | 10 +- .github/scripts/format-inline-bash.py | 403 ++++++++++++++++++ .github/workflows/build-nix-image.yml | 4 +- .github/workflows/check-pr-description.yml | 10 +- .github/workflows/check-pr-title.yml | 2 +- .github/workflows/on-pr.yml | 8 +- .github/workflows/pre-commit.yml | 2 +- .../workflows/reusable-build-docker-image.yml | 2 +- .../workflows/reusable-build-test-config.yml | 76 ++-- .../workflows/reusable-check-levelization.yml | 10 +- .github/workflows/reusable-check-rename.yml | 10 +- .github/workflows/reusable-clang-tidy.yml | 48 +-- .github/workflows/reusable-package.yml | 2 +- .../workflows/reusable-strategy-matrix.yml | 2 +- .pre-commit-config.yaml | 13 + BUILD.md | 6 +- cspell.config.yaml | 1 + include/xrpl/protocol_autogen/README.md | 4 +- package/README.md | 18 +- 20 files changed, 533 insertions(+), 116 deletions(-) create mode 100755 .github/scripts/format-inline-bash.py diff --git a/.github/actions/build-deps/action.yml b/.github/actions/build-deps/action.yml index 9d52be1998..0891d56dfa 100644 --- a/.github/actions/build-deps/action.yml +++ b/.github/actions/build-deps/action.yml @@ -37,12 +37,12 @@ runs: run: | echo 'Installing dependencies.' conan install \ - --profile ci \ - --build="${BUILD_OPTION}" \ - --options:host='&:tests=True' \ - --options:host='&:xrpld=True' \ - --settings:all build_type="${BUILD_TYPE}" \ - --conf:all tools.build:jobs=${BUILD_NPROC} \ - --conf:all tools.build:verbosity="${LOG_VERBOSITY}" \ - --conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \ - . + --profile ci \ + --build="${BUILD_OPTION}" \ + --options:host='&:tests=True' \ + --options:host='&:xrpld=True' \ + --settings:all build_type="${BUILD_TYPE}" \ + --conf:all tools.build:jobs=${BUILD_NPROC} \ + --conf:all tools.build:verbosity="${LOG_VERBOSITY}" \ + --conf:all tools.compilation:verbosity="${LOG_VERBOSITY}" \ + . diff --git a/.github/actions/generate-version/action.yml b/.github/actions/generate-version/action.yml index 8edb7920c6..50b3166596 100644 --- a/.github/actions/generate-version/action.yml +++ b/.github/actions/generate-version/action.yml @@ -15,7 +15,7 @@ runs: shell: bash env: VERSION: ${{ github.ref_name }} - run: echo "VERSION=${VERSION}" >> "${GITHUB_ENV}" + run: echo "VERSION=${VERSION}" >>"${GITHUB_ENV}" # When a tag is not pushed, then the version (e.g. 1.2.3-b0) is extracted # from the BuildInfo.cpp file and the shortened commit hash appended to it. @@ -28,17 +28,17 @@ runs: echo 'Extracting version from BuildInfo.cpp.' VERSION="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" if [[ -z "${VERSION}" ]]; then - echo 'Unable to extract version from BuildInfo.cpp.' - exit 1 + echo 'Unable to extract version from BuildInfo.cpp.' + exit 1 fi echo 'Appending shortened commit hash to version.' SHA='${{ github.sha }}' VERSION="${VERSION}+${SHA:0:7}" - echo "VERSION=${VERSION}" >> "${GITHUB_ENV}" + echo "VERSION=${VERSION}" >>"${GITHUB_ENV}" - name: Output version id: version shell: bash - run: echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" + run: echo "version=${VERSION}" >>"${GITHUB_OUTPUT}" diff --git a/.github/scripts/format-inline-bash.py b/.github/scripts/format-inline-bash.py new file mode 100755 index 0000000000..423c78109c --- /dev/null +++ b/.github/scripts/format-inline-bash.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python3 + +""" +Format embedded shell snippets using the shfmt hook configured in +.pre-commit-config.yaml. + +Two shapes are recognised: + +* YAML workflow/action files: literal block-scalar runs (`run: |`) and + single-line runs (`run: some command`). A single-line run is upgraded to + a `run: |` block scalar if shfmt's output spans multiple lines. + +* Markdown files: ``` ```bash ``` fenced code blocks. + +Any block that shfmt cannot parse is skipped with a warning on stderr, so +the file is left untouched and surrounding blocks still get formatted. + +For each occurrence the body is dedented, written to a temp .sh file, +formatted via `pre-commit run shfmt --files ` (falling back to +`prek`), then re-indented and written back in place. + +When invoked without arguments, every .yml/.yaml under .github/ plus every +.md file in the repo is scanned. When invoked with file arguments (the +pre-commit case), only those files are processed. +""" + +from __future__ import annotations + +import re +import shutil +import subprocess +import sys +import tempfile +from dataclasses import dataclass +from pathlib import Path +from typing import Union + +REPO = Path(__file__).resolve().parents[2] + +_HOOK_RUNNER = next((cmd for cmd in ("pre-commit", "prek") if shutil.which(cmd)), None) +if _HOOK_RUNNER is None: + sys.exit("error: neither `pre-commit` nor `prek` found on PATH") + +RUN_BLOCK_RE = re.compile(r"^(?P[ \t]*(?:- )?)run:[ \t]*\|[+-]?[ \t]*$") +RUN_INLINE_RE = re.compile( + r"^(?P[ \t]*(?:- )?)run:[ \t]+" r"(?P(?!\|[+-]?[ \t]*$)\S.*?)[ \t]*$" +) +MD_BASH_OPEN_RE = re.compile(r"^(?P[ ]{0,3})`{3}bash[ \t]*$") +MD_FENCE_CLOSE_RE = re.compile(r"^[ ]{0,3}`{3,}[ \t]*$") + + +@dataclass(frozen=True) +class BlockRun: + """A `run: |` block scalar; `body_start:body_end` slices into `lines`.""" + + body_start: int + body_end: int + body_indent: int + + +@dataclass(frozen=True) +class InlineRun: + """A single-line `run: value` at `line_idx`.""" + + line_idx: int + prefix: str + value: str + + +@dataclass(frozen=True) +class MdBashBlock: + """A markdown ``` ```bash ``` fenced code block. + + `body_start:body_end` slices into the file's lines; `open_line_idx` + points at the opening fence line. + """ + + open_line_idx: int + body_start: int + body_end: int + body_indent: int + + +RunItem = Union[BlockRun, InlineRun] + + +def _scan_block_body( + lines: list[str], body_start: int, run_col: int +) -> tuple[int | None, int]: + """Locate the body of a `run: |` block scalar starting at `body_start`. + + Returns `(body_indent, scan_end)`. `scan_end` is the line index where the + outer scanner should resume. `body_indent` is `None` when no body is + present (the scalar is empty, or the next non-blank line has indent + `<= run_col`). + """ + body_indent: int | None = None + scan_end = len(lines) + for idx in range(body_start, len(lines)): + line = lines[idx] + if line.strip() == "": + continue + indent = len(line) - len(line.lstrip(" ")) + if body_indent is None: + if indent > run_col: + body_indent = indent + else: + scan_end = idx + break + elif indent < body_indent: + scan_end = idx + break + if body_indent is not None: + while scan_end > body_start and lines[scan_end - 1].strip() == "": + scan_end -= 1 + if scan_end <= body_start: + body_indent = None + return body_indent, scan_end + + +def find_run_blocks(lines: list[str]) -> list[RunItem]: + """Return run items in document order.""" + items: list[RunItem] = [] + line_idx = 0 + while line_idx < len(lines): + line = lines[line_idx] + if block_match := RUN_BLOCK_RE.match(line): + run_col = len(block_match.group("prefix")) + body_start = line_idx + 1 + body_indent, scan_end = _scan_block_body(lines, body_start, run_col) + if body_indent is not None: + items.append( + BlockRun( + body_start=body_start, + body_end=scan_end, + body_indent=body_indent, + ) + ) + line_idx = scan_end + continue + if inline_match := RUN_INLINE_RE.match(line): + items.append( + InlineRun( + line_idx=line_idx, + prefix=inline_match.group("prefix"), + value=inline_match.group("value"), + ) + ) + line_idx += 1 + return items + + +def find_md_bash_blocks(lines: list[str]) -> list[MdBashBlock]: + """Return ``` ```bash ``` fenced code blocks in document order.""" + blocks: list[MdBashBlock] = [] + line_idx = 0 + while line_idx < len(lines): + open_match = MD_BASH_OPEN_RE.match(lines[line_idx]) + if not open_match: + line_idx += 1 + continue + body_start = line_idx + 1 + close_idx = next( + ( + j + for j in range(body_start, len(lines)) + if MD_FENCE_CLOSE_RE.match(lines[j]) + ), + None, + ) + if close_idx is None: + line_idx = body_start + continue + body = lines[body_start:close_idx] + non_blank = [b for b in body if b.strip()] + body_indent = ( + min(len(b) - len(b.lstrip(" ")) for b in non_blank) + if non_blank + else len(open_match.group("indent")) + ) + blocks.append( + MdBashBlock( + open_line_idx=line_idx, + body_start=body_start, + body_end=close_idx, + body_indent=body_indent, + ) + ) + line_idx = close_idx + 1 + return blocks + + +def dedent(lines: list[str], n: int) -> list[str]: + pad = " " * n + return [ + ( + "" + if line.strip() == "" + else (line[n:] if line.startswith(pad) else line.lstrip(" ")) + ) + for line in lines + ] + + +def reindent(lines: list[str], n: int) -> list[str]: + pad = " " * n + return [pad + line if line else "" for line in lines] + + +_SHFMT_ERR_RE = re.compile(r"\.sh:\d+:\d+:\s") +_GHA_EXPR_RE = re.compile(r"\$\{\{.*?\}\}", re.DOTALL) +_GHA_PLACEHOLDER_RE = re.compile(r"__GHA_EXPR_(\d+)__") + + +def _encode_gha_exprs(text: str) -> tuple[str, list[str]]: + """Replace `${{ ... }}` expressions with bash-safe placeholder identifiers.""" + exprs: list[str] = [] + + def repl(match: re.Match[str]) -> str: + exprs.append(match.group(0)) + return f"__GHA_EXPR_{len(exprs) - 1}__" + + return _GHA_EXPR_RE.sub(repl, text), exprs + + +def _decode_gha_exprs(text: str, exprs: list[str]) -> str: + """Restore `${{ ... }}` expressions from placeholder identifiers.""" + return _GHA_PLACEHOLDER_RE.sub(lambda m: exprs[int(m.group(1))], text) + + +def shfmt_via_hook(tmp_path: Path) -> tuple[bool, str]: + # `${{ ... }}` is not valid shell, so swap it for a placeholder identifier + # that shfmt can parse, then restore it after formatting. + encoded, exprs = _encode_gha_exprs(tmp_path.read_text()) + if exprs: + tmp_path.write_text(encoded) + res = subprocess.run( + [_HOOK_RUNNER, "run", "shfmt", "--files", str(tmp_path)], + cwd=REPO, + capture_output=True, + text=True, + ) + output = res.stdout + res.stderr + # shfmt emits parse errors as "::: ". + parse_err = bool(_SHFMT_ERR_RE.search(output)) + # A non-zero exit that is neither a parse error nor pre-commit's "I had + # to modify files" signal means the hook itself failed to run (missing + # binary, install failure, bad config, ...). Surface that loudly rather + # than silently treating it as a no-op. + if ( + res.returncode != 0 + and not parse_err + and "files were modified by this hook" not in output + ): + sys.exit( + f"error: `{_HOOK_RUNNER} run shfmt` failed with exit {res.returncode}:\n{output}" + ) + if exprs and not parse_err: + tmp_path.write_text(_decode_gha_exprs(tmp_path.read_text(), exprs)) + return not parse_err, output + + +def _skip(path: Path, where: int, kind: str, output: str) -> None: + print( + f" shfmt could not parse {kind} at {path}:{where + 1} — skipped", + file=sys.stderr, + ) + print(f" {output.strip()}", file=sys.stderr) + + +def process_yaml_file(path: Path, tmp_path: Path) -> int: + text = path.read_text() + had_nl = text.endswith("\n") + lines = text.split("\n") + if had_nl: + lines = lines[:-1] + items = find_run_blocks(lines) + if not items: + return 0 + changed = 0 + # Process in reverse so earlier indices remain valid as we splice. + for item in reversed(items): + if isinstance(item, BlockRun): + body = lines[item.body_start : item.body_end] + tmp_path.write_text("\n".join(dedent(body, item.body_indent)) + "\n") + ok, output = shfmt_via_hook(tmp_path) + if not ok: + _skip(path, item.body_start, "block", output) + continue + formatted = tmp_path.read_text().rstrip("\n") + new_body = reindent(formatted.split("\n"), item.body_indent) + if new_body != body: + lines[item.body_start : item.body_end] = new_body + changed += 1 + else: + tmp_path.write_text(item.value + "\n") + ok, output = shfmt_via_hook(tmp_path) + if not ok: + _skip(path, item.line_idx, "inline run", output) + continue + formatted = tmp_path.read_text().rstrip("\n") + if formatted == item.value: + continue + formatted_lines = formatted.split("\n") + if len(formatted_lines) == 1: + lines[item.line_idx] = f"{item.prefix}run: {formatted}" + else: + body_indent = len(item.prefix) + 2 + lines[item.line_idx : item.line_idx + 1] = [ + f"{item.prefix}run: |", + *reindent(formatted_lines, body_indent), + ] + changed += 1 + new_text = "\n".join(lines) + ("\n" if had_nl else "") + if new_text != text: + path.write_text(new_text) + return changed + + +def process_md_file(path: Path, tmp_path: Path) -> int: + text = path.read_text() + had_nl = text.endswith("\n") + lines = text.split("\n") + if had_nl: + lines = lines[:-1] + blocks = find_md_bash_blocks(lines) + if not blocks: + return 0 + changed = 0 + for block in reversed(blocks): + body = lines[block.body_start : block.body_end] + tmp_path.write_text("\n".join(dedent(body, block.body_indent)) + "\n") + ok, output = shfmt_via_hook(tmp_path) + if not ok: + _skip(path, block.open_line_idx, "```bash block", output) + continue + formatted = tmp_path.read_text().rstrip("\n") + formatted_lines = formatted.split("\n") if formatted else [] + new_body = reindent(formatted_lines, block.body_indent) + if new_body != body: + lines[block.body_start : block.body_end] = new_body + changed += 1 + new_text = "\n".join(lines) + ("\n" if had_nl else "") + if new_text != text: + path.write_text(new_text) + return changed + + +def process_file(path: Path, tmp_path: Path) -> int: + if path.suffix in (".yml", ".yaml"): + return process_yaml_file(path, tmp_path) + if path.suffix == ".md": + return process_md_file(path, tmp_path) + return 0 + + +def gather_files(argv: list[str]) -> list[Path]: + """Return YAML workflow/action files and markdown files that we should + process — either the paths in `argv` or, when `argv` is empty, every + such file in the repo (skipping `external/`).""" + if argv: + candidates: list[Path] = [ + (REPO / a).resolve() if not Path(a).is_absolute() else Path(a) for a in argv + ] + else: + gh = REPO / ".github" + candidates = [ + *gh.rglob("*.yml"), + *gh.rglob("*.yaml"), + *( + p + for p in REPO.rglob("*.md") + if "external" not in p.relative_to(REPO).parts + ), + ] + return sorted( + p + for p in candidates + if p.exists() + and ( + (p.suffix in (".yml", ".yaml") and ".github" in p.parts) + or p.suffix == ".md" + ) + ) + + +def main(argv: list[str]) -> int: + files = gather_files(argv) + if not files: + return 0 + with tempfile.TemporaryDirectory(prefix="format-inline-bash-") as tmpdir: + tmp_path = Path(tmpdir) / "shfmt.sh" + total = 0 + for f in files: + n = process_file(f, tmp_path) + if n: + print(f"{f.relative_to(REPO)}: reformatted {n} block(s)") + total += n + return 1 if total else 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml index edd35132fa..bae4cfd437 100644 --- a/.github/workflows/build-nix-image.yml +++ b/.github/workflows/build-nix-image.yml @@ -100,8 +100,8 @@ jobs: - name: Create multi-arch manifests run: | - for tag in $(jq -cr '.tags[]' <<< "$DOCKER_METADATA_OUTPUT_JSON"); do - docker buildx imagetools create -t "$tag" "${tag}-amd64" "${tag}-arm64" + for tag in $(jq -cr '.tags[]' <<<"$DOCKER_METADATA_OUTPUT_JSON"); do + docker buildx imagetools create -t "$tag" "${tag}-amd64" "${tag}-arm64" done - name: Inspect image diff --git a/.github/workflows/check-pr-description.yml b/.github/workflows/check-pr-description.yml index f6eee50291..3f345a9dcb 100644 --- a/.github/workflows/check-pr-description.yml +++ b/.github/workflows/check-pr-description.yml @@ -20,11 +20,11 @@ jobs: env: PR_BODY: ${{ github.event.pull_request.body }} if: ${{ github.event_name == 'pull_request' }} - run: printenv PR_BODY > pr_body.md + run: printenv PR_BODY >pr_body.md - name: Check PR description differs from template if: ${{ github.event_name == 'pull_request' }} - run: > - python .github/scripts/check-pr-description.py - --template-file .github/pull_request_template.md - --pr-body-file pr_body.md + run: | + python .github/scripts/check-pr-description.py \ + --template-file .github/pull_request_template.md \ + --pr-body-file pr_body.md diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml index 5631950df6..79962e0620 100644 --- a/.github/workflows/check-pr-title.yml +++ b/.github/workflows/check-pr-title.yml @@ -11,4 +11,4 @@ on: jobs: check_title: if: ${{ github.event.pull_request.draft != true }} - uses: XRPLF/actions/.github/workflows/check-pr-title.yml@291206777251b4d493641b5afbdf7c23009d2988 + uses: XRPLF/actions/.github/workflows/check-pr-title.yml@cba1f0891650baf1a9c88624dc2d72573be2eb81 diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index ca715e0376..db3c8667e5 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -98,7 +98,7 @@ jobs: READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }} MERGE: ${{ github.event_name == 'merge_group' }} run: | - echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >> "${GITHUB_OUTPUT}" + echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >>"${GITHUB_OUTPUT}" cat "${GITHUB_OUTPUT}" outputs: go: ${{ steps.go.outputs.go == 'true' }} @@ -168,9 +168,9 @@ jobs: PR_URL: ${{ github.event.pull_request.html_url }} run: | gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ - /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ - -F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \ - -F "client_payload[pr_url]=${PR_URL}" + /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ + -F "client_payload[ref]=${{ needs.upload-recipe.outputs.recipe_ref }}" \ + -F "client_payload[pr_url]=${PR_URL}" passed: if: failure() || cancelled() diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 2f15b82266..de6a4f40b4 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -14,7 +14,7 @@ on: jobs: # Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks. run-hooks: - uses: XRPLF/actions/.github/workflows/pre-commit.yml@5e942d61bf32f7557a7c159cfac4712a687b3e3a + uses: XRPLF/actions/.github/workflows/pre-commit.yml@cba1f0891650baf1a9c88624dc2d72573be2eb81 with: runs_on: ubuntu-latest container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-41ec7c1" }' diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index 5b555b713f..c3795e56fa 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -53,7 +53,7 @@ jobs: env: PLATFORM: ${{ inputs.platform }} run: | - echo "arch=${PLATFORM##*/}" >> $GITHUB_OUTPUT + echo "arch=${PLATFORM##*/}" >>$GITHUB_OUTPUT - name: Set up Docker Buildx uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 4f9b926e98..31457bb892 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -113,7 +113,7 @@ jobs: - name: Set ccache log file if: ${{ inputs.ccache_enabled && runner.debug == '1' }} - run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >> "${GITHUB_ENV}" + run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >>"${GITHUB_ENV}" - name: Print build environment uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 @@ -146,11 +146,11 @@ jobs: CMAKE_ARGS: ${{ inputs.cmake_args }} run: | cmake \ - -G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - ${CMAKE_ARGS} \ - .. + -G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ + ${CMAKE_ARGS} \ + .. - name: Check protocol autogen files are up-to-date working-directory: ${{ env.BUILD_DIR }} @@ -172,10 +172,10 @@ jobs: cmake --build . --target code_gen DIFF=$(git -C .. status --porcelain -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen) if [ -n "${DIFF}" ]; then - echo "::error::Generated protocol files are out of date" - git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen - echo "${MESSAGE}" - exit 1 + echo "::error::Generated protocol files are out of date" + git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen + echo "${MESSAGE}" + exit 1 fi - name: Build the binary @@ -186,18 +186,18 @@ jobs: CMAKE_TARGET: ${{ inputs.cmake_target }} run: | cmake \ - --build . \ - --config "${BUILD_TYPE}" \ - --parallel "${BUILD_NPROC}" \ - --target "${CMAKE_TARGET}" + --build . \ + --config "${BUILD_TYPE}" \ + --parallel "${BUILD_NPROC}" \ + --target "${CMAKE_TARGET}" - name: Show ccache statistics if: ${{ inputs.ccache_enabled }} run: | ccache --show-stats -vv if [ '${{ runner.debug }}' = '1' ]; then - cat "${CCACHE_LOGFILE}" - curl ${CCACHE_REMOTE_STORAGE%|*}/status || true + cat "${CCACHE_LOGFILE}" + curl ${CCACHE_REMOTE_STORAGE%|*}/status || true fi - name: Upload the binary (Linux) @@ -214,7 +214,7 @@ jobs: working-directory: ${{ env.BUILD_DIR }} run: | set -o pipefail - ./xrpld --definitions | python3 -m json.tool > server_definitions.json + ./xrpld --definitions | python3 -m json.tool >server_definitions.json - name: Upload server definitions if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'debian-bookworm-gcc-13-amd64-release' }} @@ -231,10 +231,10 @@ jobs: run: | ldd ./xrpld if [ "$(ldd ./xrpld | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then - echo 'The binary is statically linked.' + echo 'The binary is statically linked.' else - echo 'The binary is dynamically linked.' - exit 1 + echo 'The binary is dynamically linked.' + exit 1 fi - name: Verify presence of instrumentation (Linux) @@ -250,12 +250,12 @@ jobs: run: | ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" if [[ "${CONFIG_NAME}" == *gcc* ]]; then - ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0" + ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0" fi - echo "ASAN_OPTIONS=${ASAN_OPTS}" >> ${GITHUB_ENV} - echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV} - echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV} - echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV} + echo "ASAN_OPTIONS=${ASAN_OPTS}" >>${GITHUB_ENV} + echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >>${GITHUB_ENV} + echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >>${GITHUB_ENV} + echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >>${GITHUB_ENV} - name: Run the separate tests if: ${{ !inputs.build_only }} @@ -266,9 +266,9 @@ jobs: PARALLELISM: ${{ runner.os == 'Windows' && '1' || steps.nproc.outputs.nproc }} run: | ctest \ - --output-on-failure \ - -C "${BUILD_TYPE}" \ - -j "${PARALLELISM}" + --output-on-failure \ + -C "${BUILD_TYPE}" \ + -j "${PARALLELISM}" - name: Run the embedded tests if: ${{ !inputs.build_only }} @@ -278,7 +278,7 @@ jobs: run: | set -o pipefail # Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness - [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$(( BUILD_NPROC - 2 )) + [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$((BUILD_NPROC - 2)) ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log - name: Show test failure summary @@ -287,19 +287,19 @@ jobs: WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} run: | if [ ! -d "${WORKING_DIR}" ]; then - echo "Working directory '${WORKING_DIR}' does not exist." - exit 0 + echo "Working directory '${WORKING_DIR}' does not exist." + exit 0 fi cd "${WORKING_DIR}" if [ ! -f unittest.log ]; then - echo "unittest.log not found; embedded tests may not have run." - exit 0 + echo "unittest.log not found; embedded tests may not have run." + exit 0 fi if ! grep -E "failed" unittest.log; then - echo "Log present but no failure lines found in unittest.log." + echo "Log present but no failure lines found in unittest.log." fi - name: Debug failure (Linux) if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }} @@ -317,10 +317,10 @@ jobs: BUILD_TYPE: ${{ inputs.build_type }} run: | cmake \ - --build . \ - --config "${BUILD_TYPE}" \ - --parallel "${BUILD_NPROC}" \ - --target coverage + --build . \ + --config "${BUILD_TYPE}" \ + --parallel "${BUILD_NPROC}" \ + --target coverage - name: Upload coverage report if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} diff --git a/.github/workflows/reusable-check-levelization.yml b/.github/workflows/reusable-check-levelization.yml index 4efe3e1138..b5d57a177a 100644 --- a/.github/workflows/reusable-check-levelization.yml +++ b/.github/workflows/reusable-check-levelization.yml @@ -38,9 +38,9 @@ jobs: run: | DIFF=$(git status --porcelain) if [ -n "${DIFF}" ]; then - # Print the differences to give the contributor a hint about what to - # expect when running levelization on their own machine. - git diff - echo "${MESSAGE}" - exit 1 + # Print the differences to give the contributor a hint about what to + # expect when running levelization on their own machine. + git diff + echo "${MESSAGE}" + exit 1 fi diff --git a/.github/workflows/reusable-check-rename.yml b/.github/workflows/reusable-check-rename.yml index 56a1a3e637..7aa5b80594 100644 --- a/.github/workflows/reusable-check-rename.yml +++ b/.github/workflows/reusable-check-rename.yml @@ -48,9 +48,9 @@ jobs: run: | DIFF=$(git status --porcelain) if [ -n "${DIFF}" ]; then - # Print the differences to give the contributor a hint about what to - # expect when running the renaming scripts on their own machine. - git diff - echo "${MESSAGE}" - exit 1 + # Print the differences to give the contributor a hint about what to + # expect when running the renaming scripts on their own machine. + git diff + echo "${MESSAGE}" + exit 1 fi diff --git a/.github/workflows/reusable-clang-tidy.yml b/.github/workflows/reusable-clang-tidy.yml index e01a50cf6d..8be1db5fb2 100644 --- a/.github/workflows/reusable-clang-tidy.yml +++ b/.github/workflows/reusable-clang-tidy.yml @@ -70,13 +70,13 @@ jobs: working-directory: ${{ env.BUILD_DIR }} run: | cmake \ - -G 'Ninja' \ - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - -Dtests=ON \ - -Dwerr=ON \ - -Dxrpld=ON \ - .. + -G 'Ninja' \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ + -Dtests=ON \ + -Dwerr=ON \ + -Dxrpld=ON \ + .. # clang-tidy needs headers generated from proto files - name: Build libxrpl.libpb @@ -133,7 +133,7 @@ jobs: - name: Write issue header if: ${{ steps.run_clang_tidy.outcome != 'success' }} run: | - cat > "${ISSUE_FILE}" <"${ISSUE_FILE}" < filtered-output.txt || true + # Extract lines containing 'error:', 'warning:', or 'note:' + grep -E '(error:|warning:|note:)' "${OUTPUT_FILE}" >filtered-output.txt || true - # If filtered output is empty, use original (might be a different error format) - if [ ! -s filtered-output.txt ]; then - cp "${OUTPUT_FILE}" filtered-output.txt - fi + # If filtered output is empty, use original (might be a different error format) + if [ ! -s filtered-output.txt ]; then + cp "${OUTPUT_FILE}" filtered-output.txt + fi - # Truncate if too large - head -c 60000 filtered-output.txt >> "${ISSUE_FILE}" - if [ "$(wc -c < filtered-output.txt)" -gt 60000 ]; then - echo "" >> "${ISSUE_FILE}" - echo "... (output truncated, see artifacts for full output)" >> "${ISSUE_FILE}" - fi + # Truncate if too large + head -c 60000 filtered-output.txt >>"${ISSUE_FILE}" + if [ "$(wc -c >"${ISSUE_FILE}" + echo "... (output truncated, see artifacts for full output)" >>"${ISSUE_FILE}" + fi - rm filtered-output.txt + rm filtered-output.txt else - echo "No output file found" >> "${ISSUE_FILE}" + echo "No output file found" >>"${ISSUE_FILE}" fi - name: Append issue footer if: ${{ steps.run_clang_tidy.outcome != 'success' }} run: | - cat >> "${ISSUE_FILE}" <>"${ISSUE_FILE}" <> "${GITHUB_OUTPUT}" + ./generate.py --packaging --config=linux.json >>"${GITHUB_OUTPUT}" generate-version: runs-on: ubuntu-latest diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index b1232a138f..62d65ad3fa 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -42,4 +42,4 @@ jobs: env: GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} GENERATE_OPTION: ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} - run: ./generate.py ${GENERATE_OPTION} ${GENERATE_CONFIG} >> "${GITHUB_OUTPUT}" + run: ./generate.py ${GENERATE_OPTION} ${GENERATE_CONFIG} >>"${GITHUB_OUTPUT}" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 23441c9dde..c9dec89435 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,6 +66,19 @@ repos: - id: shfmt args: [--write, --indent=4, --case-indent=true] + - repo: local + hooks: + - id: format-inline-bash-workflows + name: "format `run:` blocks in workflows/actions" + entry: ./.github/scripts/format-inline-bash.py + language: python + files: ^\.github/(workflows|actions)/.*\.ya?ml$ + - id: format-inline-bash-markdown + name: "format ```bash blocks in markdown" + entry: ./.github/scripts/format-inline-bash.py + language: python + files: \.md$ + - repo: https://github.com/streetsidesoftware/cspell-cli rev: 4643f154907327ee0a2c7038f0296e0dd77d9776 # frozen: v10.0.0 hooks: diff --git a/BUILD.md b/BUILD.md index a1163dbfcc..1d3fc8f774 100644 --- a/BUILD.md +++ b/BUILD.md @@ -151,8 +151,8 @@ git init git remote add origin git@github.com:XRPLF/conan-center-index.git git sparse-checkout init for recipe in "${recipes[@]}"; do - echo "Checking out recipe '${recipe}'..." - git sparse-checkout add recipes/${recipe} + echo "Checking out recipe '${recipe}'..." + git sparse-checkout add recipes/${recipe} done git fetch origin master git checkout master @@ -180,7 +180,7 @@ the new recipe will be automatically pulled from the official Conan Center. If you see an error similar to the following after running `conan profile show`: -```bash +```text ERROR: Invalid setting '17' is not a valid 'settings.compiler.version' value. Possible values are ['5.0', '5.1', '6.0', '6.1', '7.0', '7.3', '8.0', '8.1', '9.0', '9.1', '10.0', '11.0', '12.0', '13', '13.0', '13.1', '14', '14.0', '15', diff --git a/cspell.config.yaml b/cspell.config.yaml index f26268c804..ed936941e4 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -93,6 +93,7 @@ words: - daria - dcmake - dearmor + - dedented - deleteme - demultiplexer - deserializaton diff --git a/include/xrpl/protocol_autogen/README.md b/include/xrpl/protocol_autogen/README.md index 860ed1a242..608ffed085 100644 --- a/include/xrpl/protocol_autogen/README.md +++ b/include/xrpl/protocol_autogen/README.md @@ -15,8 +15,8 @@ Generation requires a one-time setup step to create a virtual environment and install Python dependencies, followed by running the generation target: ```bash -cmake --build . --target setup_code_gen # create venv and install dependencies (once) -cmake --build . --target code_gen # generate code +cmake --build . --target setup_code_gen # create venv and install dependencies (once) +cmake --build . --target code_gen # generate code ``` By default, `CODEGEN_VENV_DIR` points to `.venv` in the project root. The diff --git a/package/README.md b/package/README.md index 2089e32e64..867ca273b4 100644 --- a/package/README.md +++ b/package/README.md @@ -74,10 +74,10 @@ VERSION=2.4.0-local PKG_RELEASE=1 docker run --rm \ - -v "$(pwd):/src" \ - -w /src \ - "$IMAGE" \ - ./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE" + -v "$(pwd):/src" \ + -w /src \ + "$IMAGE" \ + ./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE" # Output: # build/debbuild/*.deb (DEB + dbgsym .ddeb) @@ -92,12 +92,12 @@ needed, but the host toolchain replaces the pinned CI image: ```bash cmake \ - -Dxrpld=ON \ - -Dxrpld_version=2.4.0-local \ - -Dtests=OFF \ - .. + -Dxrpld=ON \ + -Dxrpld_version=2.4.0-local \ + -Dtests=OFF \ + .. -cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL +cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL ``` The `cmake/XrplPackaging.cmake` module defines the target only if at least one From 2f3558c610ee3d446b1c68a003442e63d4abbdf5 Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 28 May 2026 10:57:29 -0400 Subject: [PATCH 43/87] ci: Run PR title and description checks on staging and release branches (#7331) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- .github/workflows/check-pr-description.yml | 13 +++++++++++-- .github/workflows/check-pr-title.yml | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-pr-description.yml b/.github/workflows/check-pr-description.yml index 3f345a9dcb..ff28220171 100644 --- a/.github/workflows/check-pr-description.yml +++ b/.github/workflows/check-pr-description.yml @@ -5,8 +5,17 @@ on: types: - checks_requested pull_request: - types: [opened, edited, reopened, synchronize, ready_for_review] - branches: [develop] + types: + - opened + - edited + - reopened + - synchronize + - ready_for_review + branches: + - develop + - "release-*" + - "release/*" + - "staging/*" jobs: check_description: diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml index 79962e0620..4b5f679df1 100644 --- a/.github/workflows/check-pr-title.yml +++ b/.github/workflows/check-pr-title.yml @@ -5,8 +5,17 @@ on: types: - checks_requested pull_request: - types: [opened, edited, reopened, synchronize, ready_for_review] - branches: [develop] + types: + - opened + - edited + - reopened + - synchronize + - ready_for_review + branches: + - develop + - "release-*" + - "release/*" + - "staging/*" jobs: check_title: From 763dd503be465e92d3ec4bf17a13b4aa366080a8 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Fri, 29 May 2026 20:16:25 -0400 Subject: [PATCH 44/87] fix: Add zero domainID check for permissionedDomain (#7362) --- .../ledger/helpers/PermissionedDEXHelpers.cpp | 12 +++++++ .../tx/transactors/dex/OfferCreate.cpp | 6 ++++ .../tx/transactors/payment/Payment.cpp | 6 ++++ src/test/app/PermissionedDEX_test.cpp | 34 +++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp b/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp index 0151c5b2df..f8653f7111 100644 --- a/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp +++ b/src/libxrpl/ledger/helpers/PermissionedDEXHelpers.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -19,6 +21,16 @@ namespace xrpl::permissioned_dex { bool accountInDomain(ReadView const& view, AccountID const& account, Domain const& domainID) { + // Avoid constructing a zero-key PermissionedDomain keylet. + // keylet::permissionedDomain(uint256) uses the DomainID as the ledger key. + if (view.rules().enabled(fixCleanup3_2_0) && domainID == beast::kZero) + { + // LCOV_EXCL_START + UNREACHABLE("xrpl::permissioned_dex::accountInDomain : domainID is zero"); + return false; + // LCOV_EXCL_STOP + } + auto const sleDomain = view.read(keylet::permissionedDomain(domainID)); if (!sleDomain) return false; diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index 8bf69d25c0..f982837c5e 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -94,6 +94,12 @@ OfferCreate::preflight(PreflightContext const& ctx) if (tx.isFlag(tfHybrid) && !tx.isFieldPresent(sfDomainID)) return temINVALID_FLAG; + // A zero DomainID is invalid for a PermissionedDomain ledger entry because + // keylet::permissionedDomain(uint256) uses the DomainID as the ledger key. + if (auto const domainID = tx[~sfDomainID]; + ctx.rules.enabled(fixCleanup3_2_0) && domainID && *domainID == beast::kZero) + return temMALFORMED; + bool const bImmediateOrCancel(tx.isFlag(tfImmediateOrCancel)); bool const bFillOrKill(tx.isFlag(tfFillOrKill)); diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index 1848d34786..d7ad5d3b3c 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -125,6 +125,12 @@ Payment::preflight(PreflightContext const& ctx) if (!mpTokensV2 && isDstMPT && ctx.tx.isFieldPresent(sfPaths)) return temMALFORMED; + // A zero DomainID is invalid for a PermissionedDomain ledger entry because + // keylet::permissionedDomain(uint256) uses the DomainID as the ledger key. + if (auto const domainID = tx[~sfDomainID]; + ctx.rules.enabled(fixCleanup3_2_0) && domainID && *domainID == beast::kZero) + return temMALFORMED; + bool const partialPaymentAllowed = tx.isFlag(tfPartialPayment); bool const limitQuality = tx.isFlag(tfLimitQuality); bool const defaultPathsAllowed = !tx.isFlag(tfNoRippleDirect); diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index c6e94d7994..a88cbaa868 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -197,6 +197,20 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); } + // test preflight - malformed DomainID being zero + // Only test this with fixCleanup3_2_0 enabled. Without the fix, + // an assert-enabled build can crash when Ledger::read() receives + // a zero-key PermissionedDomain keylet. + if (features[fixCleanup3_2_0]) + { + Env env(*this, features); + auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + PermissionedDEX(env); + + env(offer(bob_, XRP(10), USD(10)), Domain(uint256{}), Ter(temMALFORMED)); + env.close(); + } + // preclaim - someone outside of the domain cannot create domain offer { Env env(*this, features); @@ -396,6 +410,24 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); } + // test preflight - malformed DomainID being zero + // Only test this with fixCleanup3_2_0 enabled. Without the fix, + // an assert-enabled build can crash when Ledger::read() receives + // a zero-key PermissionedDomain keylet. + if (features[fixCleanup3_2_0]) + { + Env env(*this, features); + auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + PermissionedDEX(env); + + env(pay(bob_, alice_, USD(10)), + Path(~USD), + Sendmax(XRP(10)), + Domain(uint256{}), + Ter(temMALFORMED)); + env.close(); + } + // preclaim - cannot send payment with non existent domain { Env env(*this, features); @@ -1772,7 +1804,9 @@ public: // Test domain offer (w/o hybrid) testOfferCreate(all); + testOfferCreate(all - fixCleanup3_2_0); testPayment(all); + testPayment(all - fixCleanup3_2_0); testBookStep(all); testRippling(all); testOfferTokenIssuerInDomain(all); From 1599c1a672587a6797e6cd125bd8d319737d4418 Mon Sep 17 00:00:00 2001 From: Bart Date: Sat, 30 May 2026 14:48:59 -0400 Subject: [PATCH 45/87] refactor: Revert "perf: Remove unnecessary caches (#5439)" (#7359) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- cfg/xrpld-example.cfg | 15 +++ include/xrpl/nodestore/Database.h | 4 + .../xrpl/nodestore/detail/DatabaseNodeImp.h | 32 +++++++ .../nodestore/detail/DatabaseRotatingImp.h | 3 + src/libxrpl/nodestore/DatabaseNodeImp.cpp | 95 ++++++++++++++----- src/libxrpl/nodestore/DatabaseRotatingImp.cpp | 6 ++ src/test/app/SHAMapStore_test.cpp | 20 +++- src/xrpld/app/main/Application.cpp | 4 + src/xrpld/app/misc/SHAMapStoreImp.cpp | 26 ++++- src/xrpld/app/misc/SHAMapStoreImp.h | 4 + 10 files changed, 181 insertions(+), 28 deletions(-) diff --git a/cfg/xrpld-example.cfg b/cfg/xrpld-example.cfg index effc62c274..9e334e6f4f 100644 --- a/cfg/xrpld-example.cfg +++ b/cfg/xrpld-example.cfg @@ -953,6 +953,21 @@ # # Optional keys for NuDB and RocksDB: # +# cache_size Size of cache for database records. Default is 16384. +# Setting this value to 0 will use the default value. +# +# cache_age Length of time in minutes to keep database records +# cached. Default is 5 minutes. Setting this value to +# 0 will use the default value. +# +# Note: if cache_size or cache_age is not specified, +# default values will be used for the unspecified +# parameter. +# +# Note: the cache will not be created if online_delete +# is specified, because the rotating NodeStore does +# not use this cache). +# # fast_load Boolean. If set, load the last persisted ledger # from disk upon process start before syncing to # the network. This is likely to improve performance diff --git a/include/xrpl/nodestore/Database.h b/include/xrpl/nodestore/Database.h index ca2dde560c..438a3cc7fc 100644 --- a/include/xrpl/nodestore/Database.h +++ b/include/xrpl/nodestore/Database.h @@ -131,6 +131,10 @@ public: std::uint32_t ledgerSeq, std::function const&)>&& callback); + /** Remove expired entries from the positive and negative caches. */ + virtual void + sweep() = 0; + /** Gather statistics pertaining to read and write activities. * * @param obj Json object reference into which to place counters. diff --git a/include/xrpl/nodestore/detail/DatabaseNodeImp.h b/include/xrpl/nodestore/detail/DatabaseNodeImp.h index dd94b27075..951c60c8c7 100644 --- a/include/xrpl/nodestore/detail/DatabaseNodeImp.h +++ b/include/xrpl/nodestore/detail/DatabaseNodeImp.h @@ -22,6 +22,32 @@ public: beast::Journal j) : Database(scheduler, readThreads, config, j), backend_(std::move(backend)) { + std::optional cacheSize, cacheAge; + + if (config.exists("cache_size")) + { + cacheSize = get(config, "cache_size"); + if (cacheSize.value() < 0) + Throw("Specified negative value for cache_size"); + } + + if (config.exists("cache_age")) + { + cacheAge = get(config, "cache_age"); + if (cacheAge.value() < 0) + Throw("Specified negative value for cache_age"); + } + + if (cacheSize.has_value() || cacheAge.has_value()) + { + cache_ = std::make_shared>( + "DatabaseNodeImp", + cacheSize.value_or(0), + std::chrono::minutes(cacheAge.value_or(0)), + stopwatch(), + j); + } + XRPL_ASSERT( backend_, "xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null " @@ -73,7 +99,13 @@ public: std::uint32_t ledgerSeq, std::function const&)>&& callback) override; + void + sweep() override; + private: + // Cache for database objects. This cache is not always initialized. Check + // for null before using. + std::shared_ptr> cache_; // Persistent key/value storage std::shared_ptr backend_; diff --git a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h index 39441ef4d8..1ba9435a5f 100644 --- a/include/xrpl/nodestore/detail/DatabaseRotatingImp.h +++ b/include/xrpl/nodestore/detail/DatabaseRotatingImp.h @@ -55,6 +55,9 @@ public: void sync() override; + void + sweep() override; + private: std::shared_ptr writableBackend_; std::shared_ptr archiveBackend_; diff --git a/src/libxrpl/nodestore/DatabaseNodeImp.cpp b/src/libxrpl/nodestore/DatabaseNodeImp.cpp index a51c34079b..9323d69131 100644 --- a/src/libxrpl/nodestore/DatabaseNodeImp.cpp +++ b/src/libxrpl/nodestore/DatabaseNodeImp.cpp @@ -24,6 +24,13 @@ DatabaseNodeImp::store(NodeObjectType type, Blob&& data, uint256 const& hash, st auto obj = NodeObject::createObject(type, std::move(data), hash); backend_->store(obj); + if (cache_) + { + // After the store, replace a negative cache entry if there is one + cache_->canonicalize(hash, obj, [](std::shared_ptr const& n) { + return n->getType() == NodeObjectType::Dummy; + }); + } } void @@ -32,9 +39,25 @@ DatabaseNodeImp::asyncFetch( std::uint32_t ledgerSeq, std::function const&)>&& callback) { + if (cache_) + { + std::shared_ptr const obj = cache_->fetch(hash); + if (obj) + { + callback(obj->getType() == NodeObjectType::Dummy ? nullptr : obj); + return; + } + } Database::asyncFetch(hash, ledgerSeq, std::move(callback)); } +void +DatabaseNodeImp::sweep() +{ + if (cache_) + cache_->sweep(); +} + std::shared_ptr DatabaseNodeImp::fetchNodeObject( uint256 const& hash, @@ -42,32 +65,58 @@ DatabaseNodeImp::fetchNodeObject( FetchReport& fetchReport, bool duplicate) { - std::shared_ptr nodeObject = nullptr; - Status status = Status::Ok; + std::shared_ptr nodeObject = cache_ ? cache_->fetch(hash) : nullptr; + if (!nodeObject) + { + JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record not " + << (cache_ ? "cached" : "found"); - try - { - status = backend_->fetch(hash, &nodeObject); - } - catch (std::exception const& e) - { - JLOG(j_.fatal()) << "fetchNodeObject " << hash - << ": Exception fetching from backend: " << e.what(); - rethrow(); - } + Status status = Status::Ok; + try + { + status = backend_->fetch(hash, &nodeObject); + } + catch (std::exception const& e) + { + JLOG(j_.fatal()) << "fetchNodeObject " << hash + << ": Exception fetching from backend: " << e.what(); + rethrow(); + } - switch (status) + switch (status) + { + case Status::Ok: + if (cache_) + { + if (nodeObject) + { + cache_->canonicalizeReplaceClient(hash, nodeObject); + } + else + { + auto notFound = NodeObject::createObject(NodeObjectType::Dummy, {}, hash); + cache_->canonicalizeReplaceClient(hash, notFound); + if (notFound->getType() != NodeObjectType::Dummy) + nodeObject = notFound; + } + } + break; + case Status::NotFound: + break; + case Status::DataCorrupt: + JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted"; + break; + default: + JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " + << static_cast(status); + break; + } + } + else { - case Status::Ok: - case Status::NotFound: - break; - case Status::DataCorrupt: - JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted"; - break; - default: - JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " - << static_cast(status); - break; + JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record found in cache"; + if (nodeObject->getType() == NodeObjectType::Dummy) + nodeObject.reset(); } if (nodeObject) diff --git a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp index 24b0e2de2e..1de6a83b4c 100644 --- a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp +++ b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp @@ -113,6 +113,12 @@ DatabaseRotatingImp::store(NodeObjectType type, Blob&& data, uint256 const& hash storeStats(1, nObj->getData().size()); } +void +DatabaseRotatingImp::sweep() +{ + // Nothing to do. +} + std::shared_ptr DatabaseRotatingImp::fetchNodeObject( uint256 const& hash, diff --git a/src/test/app/SHAMapStore_test.cpp b/src/test/app/SHAMapStore_test.cpp index fc5ef02465..6e279eadb2 100644 --- a/src/test/app/SHAMapStore_test.cpp +++ b/src/test/app/SHAMapStore_test.cpp @@ -520,6 +520,25 @@ public: ///////////////////////////////////////////////////////////// // Create NodeStore with two backends to allow online deletion of data. // Normally, SHAMapStoreImp handles all these details. + auto nscfg = env.app().config().section(ConfigSection::nodeDatabase()); + + // Provide default values. + if (!nscfg.exists("cache_size")) + { + nscfg.set( + "cache_size", + std::to_string( + env.app().config().getValueFor(SizedItem::TreeCacheSize, std::nullopt))); + } + + if (!nscfg.exists("cache_age")) + { + nscfg.set( + "cache_age", + std::to_string( + env.app().config().getValueFor(SizedItem::TreeCacheAge, std::nullopt))); + } + NodeStoreScheduler scheduler(env.app().getJobQueue()); std::string const writableDb = "write"; @@ -528,7 +547,6 @@ public: auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb); static constexpr int kReadThreads = 4; - auto nscfg = env.app().config().section(ConfigSection::nodeDatabase()); auto dbr = std::make_unique( scheduler, kReadThreads, diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index a18462f0f7..508dfc8590 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -998,6 +998,10 @@ public: JLOG(journal_.debug()) << "MasterTransaction sweep. Size before: " << oldMasterTxSize << "; size after: " << masterTxCache.size(); } + { + // Sweep NodeStore database cache(s), if enabled. + getNodeStore().sweep(); + } { std::size_t const oldLedgerMasterCacheSize = getLedgerMaster().getFetchPackCacheSize(); diff --git a/src/xrpld/app/misc/SHAMapStoreImp.cpp b/src/xrpld/app/misc/SHAMapStoreImp.cpp index fc3f71c0cc..3f4bd6280f 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.cpp +++ b/src/xrpld/app/misc/SHAMapStoreImp.cpp @@ -165,6 +165,22 @@ std::unique_ptr SHAMapStoreImp::makeNodeStore(int readThreads) { auto nscfg = app_.config().section(ConfigSection::nodeDatabase()); + + // Provide default values. + if (!nscfg.exists("cache_size")) + { + nscfg.set( + "cache_size", + std::to_string(app_.config().getValueFor(SizedItem::TreeCacheSize, std::nullopt))); + } + + if (!nscfg.exists("cache_age")) + { + nscfg.set( + "cache_age", + std::to_string(app_.config().getValueFor(SizedItem::TreeCacheAge, std::nullopt))); + } + std::unique_ptr db; if (deleteInterval_ != 0u) @@ -254,6 +270,8 @@ SHAMapStoreImp::run() LedgerIndex lastRotated = stateDb_.getState().lastRotated; netOPs_ = &app_.getOPs(); ledgerMaster_ = &app_.getLedgerMaster(); + fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache()); + treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache()); if (advisoryDelete_) canDelete_ = stateDb_.getCanDelete(); @@ -542,16 +560,16 @@ SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq) // Also clear the FullBelowCache so its generation counter is bumped. // This prevents stale "full below" markers from persisting across // backend rotation/online deletion and interfering with SHAMap sync. - app_.getNodeFamily().getFullBelowCache()->clear(); + fullBelowCache_->clear(); } void SHAMapStoreImp::freshenCaches() { - if (freshenCache(*app_.getNodeFamily().getTreeNodeCache())) + if (freshenCache(*treeNodeCache_)) + return; + if (freshenCache(app_.getMasterTransaction().getCache())) return; - - freshenCache(app_.getMasterTransaction().getCache()); } void diff --git a/src/xrpld/app/misc/SHAMapStoreImp.h b/src/xrpld/app/misc/SHAMapStoreImp.h index 4f0ce04e0d..1803c15e71 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.h +++ b/src/xrpld/app/misc/SHAMapStoreImp.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -93,6 +95,8 @@ private: // as of run() or before NetworkOPs* netOPs_ = nullptr; LedgerMaster* ledgerMaster_ = nullptr; + FullBelowCache* fullBelowCache_ = nullptr; + TreeNodeCache* treeNodeCache_ = nullptr; static constexpr auto kNodeStoreName = "NodeStore"; From 47365f42203c53b723b3d209d8bcd760bf0c4822 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Sat, 30 May 2026 20:23:29 -0400 Subject: [PATCH 46/87] fix: Improve upward rounding edge cases for Number::operator/= (#7328) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com> --- include/xrpl/basics/Number.h | 94 +++++++--- src/libxrpl/basics/Number.cpp | 208 ++++++++++++++++------ src/test/basics/Number_test.cpp | 301 +++++++++++++++++++++++++++++--- 3 files changed, 506 insertions(+), 97 deletions(-) diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index 3aeb4b5bcd..93bef82a8c 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -2,12 +2,14 @@ #include +#include #include #include #include #include #include #include +#include #include #include @@ -40,6 +42,47 @@ isPowerOfTen(T value) return logTen(value).has_value(); } +namespace detail { + +/** 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 + * compile time. Doing it at runtime would be pretty wasteful and + * inefficient. + */ +constexpr std::size_t kInt64Digits = 20; +consteval std::array +buildPowersOfTen() +{ + std::array result{}; + + std::uint64_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) + 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"); + + return result; +} + +} // namespace detail + +constexpr std::array kPowerOfTen = detail::buildPowersOfTen(); + +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); + /** MantissaRange defines a range for the mantissa of a normalized Number. * * The mantissa is in the range [min, max], where @@ -76,6 +119,7 @@ isPowerOfTen(T value) struct MantissaRange final { using rep = std::uint64_t; + enum class MantissaScale { Small, // LargeLegacy can be removed when fixCleanup3_2_0 is retired @@ -89,19 +133,15 @@ struct MantissaRange final Enabled = true, }; - explicit constexpr MantissaRange(MantissaScale scale) - : min(getMin(scale)) - , cuspRoundingFixEnabled(isCuspFixEnabled(scale)) - , log(logTen(min).value_or(-1)) - , scale(scale) + explicit constexpr MantissaRange(MantissaScale sc) : scale(sc) { } - rep min; - rep max{(min * 10) - 1}; - CuspRoundingFix cuspRoundingFixEnabled; - int log; - MantissaScale scale; + MantissaScale const scale; + 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); @@ -110,23 +150,35 @@ struct MantissaRange final getAllScales(); private: - static constexpr rep - getMin(MantissaScale scale) + static constexpr int + getExponent(MantissaScale scale) { switch (scale) { case MantissaScale::Small: - return 1'000'000'000'000'000ULL; + return 15; case MantissaScale::LargeLegacy: case MantissaScale::Large: - return 1'000'000'000'000'000'000ULL; + return 18; + // LCOV_EXCL_START 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 + throw std::runtime_error("Unknown mantissa scale"); + // LCOV_EXCL_STOP } } + // Keep this function for future use with different ways to compute + // the ranges. + static constexpr rep + getMin(MantissaScale scale, int exponent) + { + if (exponent < 0 || exponent >= kPowerOfTen.size()) + throw std::runtime_error("Invalid exponent"); // LCOV_EXCL_LINE + return kPowerOfTen[exponent]; + } + static constexpr CuspRoundingFix isCuspFixEnabled(MantissaScale scale) { @@ -477,8 +529,7 @@ public: template < auto MinMantissa, auto MaxMantissa, - Integral64 T = std::decay_t, - Integral64 TMax = std::decay_t> + Integral64 T = std::decay_t> [[nodiscard]] std::pair normalizeToRange() const; @@ -519,7 +570,8 @@ private: int& exponent, MantissaRange::rep const& minMantissa, MantissaRange::rep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled); + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, + bool dropped); [[nodiscard]] bool isnormal() const noexcept; @@ -725,16 +777,18 @@ Number::isnormal() const noexcept kMinExponent <= exponent_ && exponent_ <= kMaxExponent); } -template +template std::pair Number::normalizeToRange() const { static_assert(std::is_same_v || std::is_same_v); - static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); auto constexpr kMIN = static_cast(MinMantissa); auto constexpr kMAX = static_cast(MaxMantissa); static_assert(kMIN > 0); static_assert(kMIN % 10 == 0); + static_assert(isPowerOfTen(kMIN)); static_assert(kMAX % 10 == 9); static_assert((kMAX + 1) / 10 == kMIN); diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 11f5934b04..275d82d8c9 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -178,6 +178,10 @@ public: setPositive() noexcept; void setNegative() noexcept; + // Should only be called by doNormalize, and then only for division + // operations with remainders. + void + setDropped() noexcept; [[nodiscard]] bool isNegative() const noexcept; @@ -250,6 +254,12 @@ Number::Guard::setNegative() noexcept sbit_ = 1; } +inline void +Number::Guard::setDropped() noexcept +{ + xbit_ = 1; +} + inline bool Number::Guard::isNegative() const noexcept { @@ -398,7 +408,7 @@ Number::Guard::doRoundUp( // _don't_ increment the mantissa. Instead, divide and round recursively. It should // be impossible to recurse more than once, because once the mantissa is divided by // 10, it will be _well_ under maxMantissa and kMaxRep, so adding 1 will have no - // change of bringing it back over. + // chance of bringing it back over. doDropDigit(mantissa, exponent); XRPL_ASSERT_PARTS( safeToIncrement(mantissa), @@ -512,8 +522,6 @@ Number::one() return Number{false, range.min, -range.log, Number::Unchecked{}}; } -// Use the member names in this static function for now so the diff is cleaner -// TODO: Rename the function parameters to get rid of the "_" suffix template void doNormalize( @@ -522,7 +530,8 @@ doNormalize( int& exponent, MantissaRange::rep const& minMantissa, MantissaRange::rep const& maxMantissa, - MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) + MantissaRange::CuspRoundingFix cuspRoundingFixEnabled, + bool dropped) { static constexpr auto kMinExponent = Number::kMinExponent; static constexpr auto kMaxExponent = Number::kMaxExponent; @@ -547,6 +556,8 @@ doNormalize( Guard g; if (negative) g.setNegative(); + if (dropped) + g.setDropped(); while (m > maxMantissa) { if (exponent >= kMaxExponent) @@ -611,7 +622,12 @@ Number::normalize( internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); + // 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); + // LCOV_EXCL_STOP } template <> @@ -624,7 +640,12 @@ Number::normalize( internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); + // 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); + // LCOV_EXCL_STOP } template <> @@ -637,7 +658,8 @@ Number::normalize( internalrep const& maxMantissa, MantissaRange::CuspRoundingFix cuspRoundingFixEnabled) { - doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled); + doNormalize( + negative, mantissa, exponent, minMantissa, maxMantissa, cuspRoundingFixEnabled, false); } void @@ -838,7 +860,9 @@ Number::operator/=(Number const& y) return *this; // n* = numerator // d* = denominator - // *p = negative (positive?) + // z* = result (quotient) + // *p = negative (p for positive, even though the value means not + // positive?) // *s = sign // *m = mantissa // *e = exponent @@ -850,71 +874,155 @@ Number::operator/=(Number const& y) bool const dp = y.negative_; int const ds = (dp ? -1 : 1); - auto dm = y.mantissa_; - auto de = y.exponent_; + // Create the denominator as 128-bit unsigned, since that's what we + // need to work with. + uint128_t const dm = static_cast(y.mantissa_); + auto const de = y.exponent_; auto const& range = kRange.get(); auto const& minMantissa = range.min; auto const& maxMantissa = range.max; auto const cuspRoundingFixEnabled = range.cuspRoundingFixEnabled; - // Shift by 10^17 gives greatest precision while not overflowing - // uint128_t or the cast back to int64_t - // TODO: Can/should this be made bigger for largeRange? - // log(2^128,10) ~ 38.5 - // largeRange.log = 18, fits in 10^19 - // f can be up to 10^(38-19) = 10^19 safely - bool const small = Number::getMantissaScale() == MantissaRange::MantissaScale::Small; - uint128_t const f = small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000ULL; - XRPL_ASSERT_PARTS(f >= minMantissa * 10, "Number::operator/=", "factor expected size"); + // Division operates on two large integers (16-digit for small + // mantissas, 19-digit for large) using integer math. If the values + // were just divided directly, the result would be only ever be one + // digit or zero - not very useful. + // e.g. 9'876'543'210'987'654 / 1'234'567'890'123'456 = 8 + // 1'234'567'890'123'456 / 9'876'543'210'987'654 = 0 + // Introduce a power-of-ten multiplication factor for the numerator + // which will ensure the result has a meaningful number of digits. + // + // Consider numbers with a 2-digit mantissa: + // * Assume both numbers have an exponent of 0, using "ToNearest" rounding + // * 23 / 67 = 0 + // * Use a factor of 10^4 + // * 230'000 / 67 = 3432 with an exponent of -4 + // * The normalized result will be 34, exponent -2, or 0.34 + // + // The most extreme results are 10/99 and 99/10 + // * 100'000 / 99 = 1'010e-4 = 10e-2 or 0.10 + // * 990'000 / 10 = 99'000e-4 = 99e-1 or 9.9 + // + // Note that the computations give 2 or 3 digits after the + // decimal point to determine which way to round for most scenarios. + // + // For small mantissas (where the MantissaRange.log == 15), shifting by 10^17 gives sufficient + // precision while not overflowing uint128_t or the cast back to int64_t. (This is legacy + // behavior, which must not be changed.) + // + // For large mantissas (where the MantissaRange.log == 18), a shift by 10^20 would be optimal + // for most scenarios. However, larger mantissa values would overflow 2^128. + // + // * log(2^128,10) ~ 38.5 + // * largeRange.log = 18, fits in 10^19 + // * The expanded numerator must fit in 10^38 + // * f not be more than 10^(38-19) = 10^19 safely + // + // So, we do the division into stages: + // + // Stage 1: Use the same factor of 10^17, for the initial division. This + // will frequently not result in a whole number quotient. + // + // Stage 2: If there is a remainder from the first step, repeat the + // process with a "correction" factor of 10^5. Shift the + // result of Stage 1 over by 5 places, and add the second result to it. + // 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 + // 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. + // This is only relevant when rounding away from zero (Upward for + // positive numbers, Downward for negative), or if the "regular" + // remainder is exactly 0.5 for "ToNearest". This will give the + // rounding the most accurate result possible, as if infinite + // precision was used in the initial calculation. - // unsigned denominator - auto const dmu = static_cast(dm); - // correctionFactor can be anything between 10 and f, depending on how much - // extra precision we want to only use for rounding with the - // largeRange. Three digits seems like plenty, and is more than - // the smallRange uses. - uint128_t const correctionFactor = 1'000; + // Stage 1: Do the initial division with a factor of 10^17. + auto constexpr factorExponent = 17; + + uint128_t constexpr f = kPowerOfTen[factorExponent]; auto const numerator = uint128_t(nm) * f; - auto zm = numerator / dmu; - auto ze = ne - de - (small ? 17 : 19); - bool zn = (ns * ds) < 0; - if (!small) + auto zm = numerator / dm; + auto ze = ne - de - factorExponent; + bool zp = (ns * ds) < 0; + // dropped is used in the same way as Guard::xbit_. In the case of + // division, it indicates if there's any remainder left over after + // we have been as precise as reasonable. If there is, it would be as + // if we were using infinite precision math, and a non-zero digit + // had been shifted off the end of the result when normalizing. + bool dropped = false; + + if (range.scale != MantissaRange::MantissaScale::Small) { - // Virtually multiply numerator by correctionFactor. Since that would - // overflow in the existing uint128_t, we'll do that part separately. + // Stage 2 + // + // If there is a remainder, treat it as a secondary numerator. + // Multiply by correctionFactor separately from stage 1. // The math for this would work for small mantissas, but we need to - // preserve existing behavior. + // preserve legacy behavior. // // Consider: - // ((numerator * correctionFactor) / dmu) / correctionFactor - // = ((numerator / dmu) * correctionFactor) / correctionFactor) + // ((numerator * correctionFactor) / dm) / correctionFactor + // = ((numerator / dm) * correctionFactor) / correctionFactor) // // But that assumes infinite precision. With integer math, this is // equivalent to // - // = ((numerator / dmu * correctionFactor) - // + ((numerator % dmu) * correctionFactor) / dmu) / correctionFactor + // = ((numerator / dm * correctionFactor) + // + ((numerator % dm) * correctionFactor) / dm) / correctionFactor + // = ((zm * correctionFactor) + // + (remainder * correctionFactor) / dm) / correctionFactor // - // We have already set `mantissa_ = numerator / dmu`. Now we - // compute `remainder = numerator % dmu`, and if it is - // nonzero, we do the rest of the arithmetic. If it's zero, we can skip - // it. - auto const remainder = (numerator % dmu); + // The trick is that multiplication by correctionFactor is done on the mantissa, but + // division by correctionFactor is done by modifying the exponent, so no precision is lost + // until we normalize. + // + // If remainder is zero, we can skip this stage entirely because + // the first stage gave an exact answer. + auto constexpr correctionExponent = 5; + uint128_t constexpr correctionFactor = kPowerOfTen[correctionExponent]; + static_assert(factorExponent + correctionExponent == 22); + + auto const remainder = (numerator % dm); if (remainder != 0) { - zm *= correctionFactor; - auto const correction = remainder * correctionFactor / dmu; - zm += correction; - // divide by 1000 by moving the exponent, so we don't lose the - // integer value we just computed - ze -= 3; + auto const partialNumerator = remainder * correctionFactor; + auto const correction = partialNumerator / dm; + + // If the correction is zero, we do not have to make any + // modifications to z*, because it will not have any + // effect on the final result. (We'd be adding a bunch of + // zeros to the end of zm that would just be removed in + // normalize.) However, if that is the case, then Stage 3 is + // even more important for accuracy. + if (correction != 0) + { + zm *= correctionFactor; + // divide by the correctionFactor by moving the exponent, so we don't lose the + // integer value we just computed + ze -= correctionExponent; + + zm += correction; + } + + // Stage 3: If there's still anything left, and the cusp + // rounding fix is enabled, flag if there is still + // a remainder from stage 2. + bool const useTrailingRemainder = + cuspRoundingFixEnabled == MantissaRange::CuspRoundingFix::Enabled; + if (useTrailingRemainder) + { + dropped = partialNumerator % dm != 0; + } } } - normalize(zn, zm, ze, minMantissa, maxMantissa, cuspRoundingFixEnabled); - negative_ = zn; + doNormalize(zp, zm, ze, minMantissa, maxMantissa, cuspRoundingFixEnabled, dropped); + negative_ = zp; mantissa_ = static_cast(zm); exponent_ = ze; XRPL_ASSERT_PARTS(isnormal(), "xrpl::Number::operator/=", "result is normalized"); diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 26060c70e9..81019970ad 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -6,11 +6,14 @@ #include #include +// NOLINTNEXTLINE(misc-include-cleaner) +#include #include #include #include #include +#include #include #include #include @@ -40,6 +43,40 @@ class Number_test : public beast::unit_test::Suite return out; } + using dec = boost::multiprecision::cpp_dec_float_50; + + template + static T + pow10(int n) + { + if (n == 0) + return 1; + if (n == 1) + return 10; + + if (n > 1) + { + auto r = pow10(n / 2); + r *= r; + if (n % 2 != 0) + r *= 10; + return r; + } + + // n < 0 + T p = 1; + p /= pow10(-n); + return p; + } + + static std::string + fmt(dec const& v) + { + std::ostringstream os; + os << std::setprecision(40) << v; + return os.str(); + } + public: void testZero() @@ -1589,39 +1626,249 @@ public: void testUpwardRoundsDown() { - testcase << "upward rounding produces a value below exact at kMaxRep cusp"; + auto const scale = Number::getMantissaScale(); + { + testcase << "upward rounding produces a value below exact at kMaxRep cusp " + << to_string(scale); - NumberMantissaScaleGuard const mg{MantissaRange::MantissaScale::Large}; - NumberRoundModeGuard const rg{Number::RoundingMode::Upward}; + NumberRoundModeGuard const rg{Number::RoundingMode::Upward}; - constexpr std::int64_t kAValue = 1'000'000'000'000'049'863LL; - constexpr std::int64_t kBValue = 9'223'372'036'854'315'903LL; + constexpr std::int64_t kAValue = 1'000'000'000'000'049'863LL; + constexpr std::int64_t kBValue = 9'223'372'036'854'315'903LL; - Number const a = kAValue; - Number const b = kBValue; - Number const product = a * b; + Number const a = kAValue; + Number const b = kBValue; + Number const product = a * b; - // Exact reference in BigInt. - BigInt const exactProduct = BigInt(kAValue) * BigInt(kBValue); + // Exact reference in BigInt. + 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; + // What Number actually stored. + BigInt storedValue = BigInt(product.mantissa()); + for (int i = 0; i < product.exponent(); ++i) + storedValue *= 10; - BigInt const signedDifference = storedValue - exactProduct; + BigInt const signedDifference = storedValue - exactProduct; - log << "\n" - << " a = " << fmt(BigInt(kAValue)) << "\n" - << " b = " << fmt(BigInt(kBValue)) << "\n" - << " exact a*b = " << fmt(exactProduct) << "\n" - << " stored = " << fmt(storedValue) << "\n" - << " stored - exact = " << fmt(signedDifference) << "\n" - << " upward = " << (signedDifference >= 0 ? "held" : "VIOLATED") << "\n"; + log << "\n" + << " a = " << fmt(BigInt(kAValue)) << "\n" + << " b = " << fmt(BigInt(kBValue)) << "\n" + << " exact a*b = " << fmt(exactProduct) << "\n" + << " stored = " << fmt(storedValue) << "\n" + << " stored - exact = " << fmt(signedDifference) << "\n" + << " upward = " << (signedDifference >= 0 ? "held" : "VIOLATED") << "\n" + << " stored.mantissa = " << product.mantissa() << "\n" + << " stored.exponent = " << product.exponent() << "\n"; + log.flush(); - BEAST_EXPECT(signedDifference >= 0); - BEAST_EXPECT(product.mantissa() == (std::numeric_limits::max() / 10) + 1); - BEAST_EXPECT(product.exponent() == 19); + switch (scale) + { + case MantissaRange::MantissaScale::Large: + BEAST_EXPECT(signedDifference >= 0); + BEAST_EXPECT(signedDifference < pow10(product.exponent())); + BEAST_EXPECT( + product.mantissa() == (std::numeric_limits::max() / 10) + 1); + BEAST_EXPECT(product.exponent() == 19); + break; + + case MantissaRange::MantissaScale::LargeLegacy: + BEAST_EXPECT(signedDifference < 0); + BEAST_EXPECT( + product.mantissa() == + (std::numeric_limits::max() / 100) * 100); + BEAST_EXPECT(product.exponent() == 18); + break; + + case MantissaRange::MantissaScale::Small: + // The seemingly weird rounding here is because + // a & b are both normalized, and both round up when + // being converted to Number, so you're really + // getting 1_000_000_000_000_050 * 9_223_372_036_854_316 + BEAST_EXPECT(signedDifference >= 0); + BEAST_EXPECT( + product.mantissa() == + (std::numeric_limits::max() / 1000) + 3); + BEAST_EXPECT(product.exponent() == 21); + break; + } + } + + { + /* Companion regression for the kMaxRep cusp behavior, but for + * `operator/=` on the cusp-fix-ENABLED `Large` scale. + * + * Before the dropped-remainder fix, `operator/=` with Upward + * rounding could return a value STRICTLY LESS than the exact quotient, + * violating Upward's directional invariant. + * + * Mechanism (fix-enabled path): + * 1. `operator/=` computes `numerator = nm * 10^17` and + * `zm = numerator / dm` (integer division, truncates remainder). + * 2. If `remainder != 0`, the correction block runs: + * zm *= 100000 + * correction = (remainder * 100000) / dm // also truncates + * zm += correction + * ze -= 5 + * The truncation in `correction` discards a sub-1/100000 residual. + * 3. `normalize`'s shift loop reduces zm to fit, but the discarded + * residual is BELOW the Guard's visibility, so the Guard sees fraction = 0. + * 4. Under Upward + positive, `round()` returns -1 (no round-up), and + * the algorithm returns the truncated zm + */ + testcase << "operator/= Upward on Large returns value < truth " << to_string(scale); + + NumberRoundModeGuard const roundGuard{Number::RoundingMode::Upward}; + + constexpr std::int64_t aValue = 2LL; + constexpr std::int64_t bValue = 1'000'000'000'000'000'007LL; + // bValue = 10^18 + 7 (prime, in [minMantissa, kMaxRep]). + + Number const a{aValue, 0}; + Number const b{bValue, 0}; + Number const quotient = a / b; + + dec const exact = dec(aValue) / dec(bValue); + dec const stored = dec(quotient.mantissa()) * pow10(quotient.exponent()); + dec const diff = stored - exact; + + log << "\n" + << " a = " << aValue << "\n" + << " b = " << bValue << "\n" + << " exact a/b = " << fmt(exact) << "\n" + << " stored a/b = " << fmt(stored) << "\n" + << " stored - exact = " << fmt(diff) + << " (negative => Upward gave value BELOW truth)\n" + << " quotient.mantissa = " << quotient.mantissa() << "\n" + << " quotient.exponent = " << quotient.exponent() << "\n"; + log.flush(); + + // Upward invariant: stored >= exact. Bug: stored < exact. + switch (scale) + { + case MantissaRange::MantissaScale::Large: + BEAST_EXPECT(stored >= exact); + BEAST_EXPECT(diff < pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::LargeLegacy: + BEAST_EXPECT(stored < exact); + BEAST_EXPECT(diff >= -pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::Small: + // Small mantissa doesn't have the correction for + // dropped remainders + BEAST_EXPECT(stored < exact); + break; + } + } + { + /* Companion test case for Upward positive operator/=: Downward negative + */ + testcase << "operator/= Downward on Large returns value < truth " << to_string(scale); + + NumberRoundModeGuard const roundGuard{Number::RoundingMode::Downward}; + + constexpr std::int64_t aValue = -2LL; + constexpr std::int64_t bValue = 1'000'000'000'000'000'007LL; + // bValue = 10^18 + 7 (prime, in [minMantissa, kMaxRep]). + + Number const a{aValue, 0}; + Number const b{bValue, 0}; + Number const quotient = a / b; + + dec const exact = dec(aValue) / dec(bValue); + dec const stored = dec(quotient.mantissa()) * pow10(quotient.exponent()); + dec const diff = stored - exact; + + log << "\n" + << " a = " << aValue << "\n" + << " b = " << bValue << "\n" + << " exact a/b = " << fmt(exact) << "\n" + << " stored a/b = " << fmt(stored) << "\n" + << " stored - exact = " << fmt(diff) + << " (positive => Downward gave value ABOVE truth)\n" + << " quotient.mantissa = " << quotient.mantissa() << "\n" + << " quotient.exponent = " << quotient.exponent() << "\n"; + log.flush(); + + // invariant: stored <= exact. Bug: stored > exact. + switch (scale) + { + case MantissaRange::MantissaScale::Large: + BEAST_EXPECT(stored <= exact); + BEAST_EXPECT(diff > -pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::LargeLegacy: + BEAST_EXPECT(stored > exact); + BEAST_EXPECT(diff <= pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::Small: + // Small mantissa doesn't have the correction for + // dropped remainders + BEAST_EXPECT(stored < exact); + break; + } + } + { + /* Companion test case for Upward positive operator/=: ToNearest + * + * With ToNearest, if the dropped digits are exactly "5", then the mantissa will be + * rounded to even. The numbers below result in a value where the unrounded mantissa + * ends in an even digit, and "infinite precision" would drop + * "500000000000000000145...", but doNormalize only sees "5". Without the rounding fix, + * doNormalize rounds down to the even value. With the rounding fix, doNormalize knows + * there are more digits beyond "5", and so rounds _up_ to the odd value. + */ + testcase << "operator/= ToNearest on Large returns value < truth " << to_string(scale); + + NumberRoundModeGuard const roundGuard{Number::RoundingMode::ToNearest}; + + constexpr std::int64_t aValue = 1'269'917'268'816'087'809LL; + constexpr std::int64_t bValue = 3'458'525'013'821'685'511LL; + // bValue = 10^18 + 7 (prime, in [minMantissa, kMaxRep]). + + Number const a{aValue, 0}; + Number const b{bValue, 0}; + Number const quotient = a / b; + + dec const exact = dec(aValue) / dec(bValue); + dec const stored = dec(quotient.mantissa()) * pow10(quotient.exponent()); + dec const diff = stored - exact; + + log << "\n" + << " a = " << aValue << "\n" + << " b = " << bValue << "\n" + << " exact a/b = " << fmt(exact) << "\n" + << " stored a/b = " << fmt(stored) << "\n" + << " stored - exact = " << fmt(diff) + << " (negative => ToNearest gave value BELOW truth)\n" + << " quotient.mantissa = " << quotient.mantissa() << "\n" + << " quotient.exponent = " << quotient.exponent() << "\n"; + log.flush(); + + // invariant: stored >= exact. Bug: stored < exact. + switch (scale) + { + case MantissaRange::MantissaScale::Large: + BEAST_EXPECT(stored >= exact); + BEAST_EXPECT(diff < pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::LargeLegacy: + BEAST_EXPECT(stored < exact); + BEAST_EXPECT(diff >= -pow10(quotient.exponent())); + break; + + case MantissaRange::MantissaScale::Small: + // Small mantissa doesn't have the correction for + // dropped remainders + BEAST_EXPECT(stored < exact); + break; + } + } } void @@ -1651,9 +1898,9 @@ public: testTruncate(); testRounding(); testInt64(); + + testUpwardRoundsDown(); } - // This test sets its own number range - testUpwardRoundsDown(); } }; From 99431d78335919fce5910d01966ed8f55b98572a Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Mon, 1 Jun 2026 00:54:23 +0200 Subject: [PATCH 47/87] fix: Pin overpayment principal reduction to exact on-grid value (#7360) --- include/xrpl/ledger/helpers/LendingHelpers.h | 1 + src/libxrpl/ledger/helpers/LendingHelpers.cpp | 138 ++++--- src/test/app/LendingHelpers_test.cpp | 182 +++++---- src/test/app/Loan_test.cpp | 362 ++++++++++++++++++ 4 files changed, 552 insertions(+), 131 deletions(-) diff --git a/include/xrpl/ledger/helpers/LendingHelpers.h b/include/xrpl/ledger/helpers/LendingHelpers.h index b7a1ed6bf2..32f94ee277 100644 --- a/include/xrpl/ledger/helpers/LendingHelpers.h +++ b/include/xrpl/ledger/helpers/LendingHelpers.h @@ -461,6 +461,7 @@ loanAccruedInterest( ExtendedPaymentComponents computeOverpaymentComponents( + Rules const& rules, Asset const& asset, int32_t const loanScale, Number const& overpayment, diff --git a/src/libxrpl/ledger/helpers/LendingHelpers.cpp b/src/libxrpl/ledger/helpers/LendingHelpers.cpp index 9cda5905c9..15ebf33e46 100644 --- a/src/libxrpl/ledger/helpers/LendingHelpers.cpp +++ b/src/libxrpl/ledger/helpers/LendingHelpers.cpp @@ -559,15 +559,34 @@ tryOverpayment( << ", new total value: " << newLoanProperties.loanState.valueOutstanding << ", first payment principal: " << newLoanProperties.firstPaymentPrincipal; - // Calculate what the new loan state should be with the new periodic payment - // including rounding errors - auto const newTheoreticalState = computeTheoreticalLoanState( - rules, - newLoanProperties.periodicPayment, - periodicRate, - paymentRemaining, - managementFeeRate) + - errors; + // Calculate what the new loan state should be with the new periodic payment, + // including the preserved rounding errors. + + auto const newTheoreticalState = [&]() { + auto const state = computeTheoreticalLoanState( + rules, + newLoanProperties.periodicPayment, + periodicRate, + paymentRemaining, + managementFeeRate) + + errors; + + if (!rules.enabled(fixCleanup3_2_0)) + return state; + + // The new principal is known exactly: it is reduced by the overpayment's + // principal portion. computeTheoreticalLoanState instead derives the + // principal -- and, from it, the management fee and interest -- via a + // lossy (P * factor) / factor round-trip. Pin the principal to the exact + // value and re-derive the management fee from the exact interest gross + // (value - principal), so the intermediate state is fully consistent with + // the exact principal rather than the one-scale-unit-high round-trip. + Number const principal = + roundedOldState.principalOutstanding - overpaymentComponents.trackedPrincipalDelta; + Number const managementFee = + tenthBipsOfValue(state.valueOutstanding - principal, managementFeeRate); + return constructLoanState(state.valueOutstanding, principal, managementFee); + }(); JLOG(j.debug()) << "new theoretical value: " << newTheoreticalState.valueOutstanding << ", principal: " << newTheoreticalState.principalOutstanding @@ -762,13 +781,6 @@ doOverpayment( // The proxies still hold the original (pre-overpayment) values, which // allows us to compute deltas and verify they match what we expect // from the overpaymentComponents and loanPaymentParts. - - XRPL_ASSERT_PARTS( - overpaymentComponents.trackedPrincipalDelta == - principalOutstandingProxy - newRoundedLoanState.principalOutstanding, - "xrpl::detail::doOverpayment", - "principal change agrees"); - JLOG(j.debug()) << "valueChange: " << loanPaymentParts.valueChange << ", totalValue before: " << *totalValueOutstandingProxy << ", totalValue after: " << newRoundedLoanState.valueOutstanding @@ -780,35 +792,50 @@ doOverpayment( << overpaymentComponents.trackedPrincipalDelta - (totalValueOutstandingProxy - newRoundedLoanState.valueOutstanding); - // The valueChange returned by tryOverpayment satisfies - // valueChange = (newInterestDue - oldInterestDue) + untrackedInterest. - // Using the loan-state identity v = p + i + m and the adjacent - // `principal change agrees` assertion (dp = oldP - newP), this - // rearranges into three independently-computable terms: - // - // 1. TVO change beyond what principal repayment alone explains: - // newTVO - (oldTVO - dp) - // 2. Management fee released by re-amortization (positive when - // mfee decreased; zero when managementFeeRate == 0): - // oldMfee - newMfee - // 3. The overpayment's penalty interest part (= untrackedInterest - // for the overpayment path; see computeOverpaymentComponents): - // trackedInterestPart() - [[maybe_unused]] Number const tvoChange = newRoundedLoanState.valueOutstanding - - (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta); - [[maybe_unused]] Number const managementFeeReleased = - managementFeeOutstandingProxy - newRoundedLoanState.managementFeeDue; - [[maybe_unused]] Number const interestPart = overpaymentComponents.trackedInterestPart(); + // The three assertions below are invariants that only hold once + // fixCleanup3_2_0 pins the new principal to the exact reduction + // (oldPrincipal - trackedPrincipalDelta). Before the amendment, the lossy + // (P * factor) / factor round-trip can leave the new principal one + // scale-unit high, so these equalities do not hold on the pre-amendment + // code path and must be gated to match the fix they verify. + if (rules.enabled(fixCleanup3_2_0)) + { + // The valueChange returned by tryOverpayment satisfies + // valueChange = (newInterestDue - oldInterestDue) + untrackedInterest. + // Using the loan-state identity v = p + i + m and the adjacent + // `principal change agrees` assertion (dp = oldP - newP), this + // rearranges into three independently-computable terms: + // + // 1. TVO change beyond what principal repayment alone explains: + // newTVO - (oldTVO - dp) + // 2. Management fee released by re-amortization (positive when + // mfee decreased; zero when managementFeeRate == 0): + // oldMfee - newMfee + // 3. The overpayment's penalty interest part (= untrackedInterest + // for the overpayment path; see computeOverpaymentComponents): + // trackedInterestPart() + [[maybe_unused]] Number const tvoChange = newRoundedLoanState.valueOutstanding - + (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta); + [[maybe_unused]] Number const managementFeeReleased = + managementFeeOutstandingProxy - newRoundedLoanState.managementFeeDue; + [[maybe_unused]] Number const interestPart = overpaymentComponents.trackedInterestPart(); - XRPL_ASSERT_PARTS( - loanPaymentParts.valueChange == tvoChange + managementFeeReleased + interestPart, - "xrpl::detail::doOverpayment", - "interest paid agrees"); + XRPL_ASSERT_PARTS( + overpaymentComponents.trackedPrincipalDelta == + principalOutstandingProxy - newRoundedLoanState.principalOutstanding, + "xrpl::detail::doOverpayment", + "principal change agrees"); - XRPL_ASSERT_PARTS( - overpaymentComponents.trackedPrincipalDelta == loanPaymentParts.principalPaid, - "xrpl::detail::doOverpayment", - "principal payment matches"); + XRPL_ASSERT_PARTS( + loanPaymentParts.valueChange == tvoChange + managementFeeReleased + interestPart, + "xrpl::detail::doOverpayment", + "interest paid agrees"); + + XRPL_ASSERT_PARTS( + overpaymentComponents.trackedPrincipalDelta == loanPaymentParts.principalPaid, + "xrpl::detail::doOverpayment", + "principal payment matches"); + } // All validations passed, so update the proxy objects (which will // modify the actual Loan ledger object) @@ -1144,11 +1171,13 @@ computePaymentComponents( // Cap each component to never exceed what's actually outstanding deltas.principal = std::min(deltas.principal, currentLedgerState.principalOutstanding); - XRPL_ASSERT_PARTS( - deltas.interest <= currentLedgerState.interestDue, - "xrpl::detail::computePaymentComponents", - "interest due delta not greater than outstanding"); - + if (fixCleanup320Enabled) + { + XRPL_ASSERT_PARTS( + deltas.interest <= currentLedgerState.interestDue, + "xrpl::detail::computePaymentComponents", + "interest due delta not greater than outstanding"); + } // Cap interest to both the outstanding amount AND what's left of the // periodic payment after principal is paid deltas.interest = std::min( @@ -1289,6 +1318,7 @@ computePaymentComponents( */ ExtendedPaymentComponents computeOverpaymentComponents( + Rules const& rules, Asset const& asset, int32_t const loanScale, Number const& overpayment, @@ -1296,10 +1326,13 @@ computeOverpaymentComponents( TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate) { - XRPL_ASSERT( - overpayment > 0 && isRounded(asset, overpayment, loanScale), - "xrpl::detail::computeOverpaymentComponents : valid overpayment " - "amount"); + if (rules.enabled(fixCleanup3_2_0)) + { + XRPL_ASSERT( + overpayment > 0 && isRounded(asset, overpayment, loanScale), + "xrpl::detail::computeOverpaymentComponents : valid overpayment " + "amount"); + } // First, deduct the fixed overpayment fee from the total amount. // This reduces the effective payment that will be applied to the loan. @@ -2055,6 +2088,7 @@ loanMakePayment( { detail::ExtendedPaymentComponents const overpaymentComponents = detail::computeOverpaymentComponents( + view.rules(), asset, loanScale, overpayment, diff --git a/src/test/app/LendingHelpers_test.cpp b/src/test/app/LendingHelpers_test.cpp index af46dd2e0f..ac8e0764fc 100644 --- a/src/test/app/LendingHelpers_test.cpp +++ b/src/test/app/LendingHelpers_test.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -513,7 +514,9 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const expectedOverpaymentManagementFee = Number{10}; // 10% of 100 auto const expectedPrincipalPortion = Number{400}; // 1,000 - 100 - 500 + Env const env{*this}; auto const components = xrpl::detail::computeOverpaymentComponents( + env.current()->rules(), iou, loanScale, overpayment, @@ -854,7 +857,13 @@ class LendingHelpers_test : public beast::unit_test::Suite Number const overpaymentAmount{50}; auto const overpaymentComponents = computeOverpaymentComponents( - asset, loanScale, overpaymentAmount, TenthBips32(0), TenthBips32(0), managementFeeRate); + env.current()->rules(), + asset, + loanScale, + overpaymentAmount, + TenthBips32(0), + TenthBips32(0), + managementFeeRate); auto const loanProperties = computeLoanProperties( env.current()->rules(), @@ -942,6 +951,7 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1037,6 +1047,7 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1138,6 +1149,7 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1247,6 +1259,7 @@ class LendingHelpers_test : public beast::unit_test::Suite auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1344,7 +1357,6 @@ class LendingHelpers_test : public beast::unit_test::Suite using namespace jtx; using namespace xrpl::detail; - Env const env{*this}; Account const issuer{"issuer"}; PrettyAsset const asset = issuer["USD"]; std::int32_t const loanScale = -5; @@ -1355,7 +1367,9 @@ class LendingHelpers_test : public beast::unit_test::Suite std::uint32_t const paymentsRemaining = 10; auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval); + Env const env{*this}; auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), asset, loanScale, Number{50, 0}, @@ -1363,87 +1377,97 @@ class LendingHelpers_test : public beast::unit_test::Suite TenthBips32(10'000), // 10% overpayment fee managementFeeRate); - auto const loanProperties = computeLoanProperties( - env.current()->rules(), - asset, - loanPrincipal, - loanInterestRate, - paymentInterval, - paymentsRemaining, - managementFeeRate, - loanScale); + struct Outcome + { + LoanPaymentParts parts; + LoanState oldState; + LoanState newState; + }; - auto const ret = tryOverpayment( - env.current()->rules(), - asset, - loanScale, - overpaymentComponents, - loanProperties.loanState, - loanProperties.periodicPayment, - periodicRate, - paymentsRemaining, - managementFeeRate, - env.journal); + // Run tryOverpayment under a given amendment set. At this (non-near-zero) + // rate computeLoanProperties is amendment-independent, so the loan state + // is identical across the amendment; only tryOverpayment's fixCleanup3_2_0 + // behaviour (the exact-principal pin and the management-fee re-derivation + // from that principal) differs. + auto run = [&](FeatureBitset features) -> std::optional { + Env const env{*this, features}; + auto const loanProperties = computeLoanProperties( + env.current()->rules(), + asset, + loanPrincipal, + loanInterestRate, + paymentInterval, + paymentsRemaining, + managementFeeRate, + loanScale); + auto const ret = tryOverpayment( + env.current()->rules(), + asset, + loanScale, + overpaymentComponents, + loanProperties.loanState, + loanProperties.periodicPayment, + periodicRate, + paymentsRemaining, + managementFeeRate, + env.journal); + if (!BEAST_EXPECT(ret)) + return std::nullopt; + return Outcome{ + .parts = ret->first, + .oldState = loanProperties.loanState, + .newState = ret->second.loanState}; + }; - BEAST_EXPECT(ret); + auto const fixedOpt = run(testableAmendments()); + auto const legacyOpt = run(testableAmendments() - fixCleanup3_2_0); + if (!fixedOpt || !legacyOpt) + { + BEAST_EXPECT(fixedOpt.has_value()); + BEAST_EXPECT(legacyOpt.has_value()); + return; + } + Outcome const& fixed = *fixedOpt; + Outcome const& legacy = *legacyOpt; - auto const& [actualPaymentParts, newLoanProperties] = *ret; - auto const& newState = newLoanProperties.loanState; + // Components that the amendment does not change. The management fee is + // charged against the overpayment interest portion first, so interest + // paid stays 4.5 and fee paid 5.5; the principal repaid is 40 in both. + auto checkCommon = [&](Outcome const& o, char const* tag) { + BEAST_EXPECTS( + (o.parts.interestPaid == Number{45, -1}), + std::string(tag) + " interestPaid " + to_string(o.parts.interestPaid)); + BEAST_EXPECTS( + (o.parts.feePaid == Number{55, -1}), + std::string(tag) + " feePaid " + to_string(o.parts.feePaid)); + BEAST_EXPECTS( + o.parts.principalPaid == 40, + std::string(tag) + " principalPaid " + to_string(o.parts.principalPaid)); + BEAST_EXPECT( + o.parts.principalPaid == + o.oldState.principalOutstanding - o.newState.principalOutstanding); + // v = p + i + m identity: the non-interest part of valueChange equals + // the interest-due change. + BEAST_EXPECT( + o.parts.valueChange - o.parts.interestPaid == + o.newState.interestDue - o.oldState.interestDue); + }; + checkCommon(fixed, "fixed"); + checkCommon(legacy, "legacy"); - // =========== VALIDATE PAYMENT PARTS =========== - - // Since there is loan management fee, the fee is charged against - // overpayment interest portion first, so interest paid remains 4.5 - BEAST_EXPECTS( - (actualPaymentParts.interestPaid == Number{45, -1}), - " interestPaid mismatch: expected 4.5, got " + - to_string(actualPaymentParts.interestPaid)); - - // With overpayment interest portion, value change should equal the - // interest decrease plus overpayment interest portion - BEAST_EXPECTS( - (actualPaymentParts.valueChange == - Number{-164737, -5} + actualPaymentParts.interestPaid), - " valueChange mismatch: expected " + - to_string(Number{-164737, -5} + actualPaymentParts.interestPaid) + ", got " + - to_string(actualPaymentParts.valueChange)); - - // While there is no overpayment fee, fee paid should equal the - // management fee charged against the overpayment interest portion - BEAST_EXPECTS( - (actualPaymentParts.feePaid == Number{55, -1}), - " feePaid mismatch: expected 5.5, got " + to_string(actualPaymentParts.feePaid)); - - BEAST_EXPECTS( - actualPaymentParts.principalPaid == 40, - " principalPaid mismatch: expected 40, got `" + - to_string(actualPaymentParts.principalPaid)); - - // =========== VALIDATE STATE CHANGES =========== - - BEAST_EXPECTS( - actualPaymentParts.principalPaid == - loanProperties.loanState.principalOutstanding - newState.principalOutstanding, - " principalPaid mismatch: expected " + - to_string( - loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + - ", got " + to_string(actualPaymentParts.principalPaid)); - - // Note that the management fee value change is not captured, as this - // value is not needed to correctly update the Vault state. - BEAST_EXPECTS( - (newState.managementFeeDue - loanProperties.loanState.managementFeeDue == - Number{-18304, -5}), - " management fee change mismatch: expected " + to_string(Number{-18304, -5}) + - ", got " + - to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue)); - - BEAST_EXPECTS( - actualPaymentParts.valueChange - actualPaymentParts.interestPaid == - newState.interestDue - loanProperties.loanState.interestDue, - " valueChange mismatch: expected " + - to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " + - to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid)); + // With fixCleanup3_2_0 the management fee is re-derived from the exact + // principal; without it, from the one-scale-unit-high round-trip + // principal. So the management fee outstanding (and hence the value + // change, via v = p + i + m) differ by exactly one scale-unit (1e-5 at + // loanScale -5) between the two paths. + BEAST_EXPECT((fixed.parts.valueChange == Number{-164738, -5} + fixed.parts.interestPaid)); + BEAST_EXPECT( + (fixed.newState.managementFeeDue - fixed.oldState.managementFeeDue == + Number{-18303, -5})); + BEAST_EXPECT((legacy.parts.valueChange == Number{-164737, -5} + legacy.parts.interestPaid)); + BEAST_EXPECT( + (legacy.newState.managementFeeDue - legacy.oldState.managementFeeDue == + Number{-18304, -5})); } public: diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index c380655563..c3b5850231 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -7580,6 +7580,366 @@ protected: attemptWithdrawShares(depositorB, sharesLpB, tesSUCCESS); } + // A residual overpayment can reduce the stored principal by one scale-unit + // *less* than computeOverpaymentComponents predicts, firing the + // "principal change agrees" XRPL_ASSERT_PARTS in doOverpayment: + // + // trackedPrincipalDelta == principalOutstanding - newPrincipalOutstanding + // + // tryOverpayment re-amortizes the loan at the reduced principal, then + // re-derives the theoretical principal from the new periodic payment via + // (P * paymentFactor) / paymentFactor. That round-trip is not exact in + // Number's 19-digit arithmetic; a positive residual pushes the recomputed + // principal a hair above the exact grid point `oldPrincipal - delta`, and + // the Upward rounding in tryOverpayment then bumps it a full scale-unit + // higher. The principal therefore drops by `delta - 1 unit`, not `delta`. + // + // Concrete case (isolated, at the tryOverpayment level): + // A 100 USD loan at the minimum non-zero rate, 3 payments, loanScale -10. + // After one regular payment (principalOutstanding 66.6666666674) a residual overpayment of + // 0.049999998 yields trackedPrincipalDelta 0.048999998 but only reduces the principal by + // 0.0489999979 (newPrincipal 66.6176666695) — short by 1e-10. + // + // With fixCleanup3_2_0, tryOverpayment pins the new principal to the exact, + // on-grid reduction (oldPrincipal - trackedPrincipalDelta) instead of the + // lossy (P*factor)/factor round-trip, so the assertion holds and the + // overpayment applies cleanly. The three "principal change agrees" / + // "interest paid agrees" / "principal payment matches" assertions are + // gated behind the same amendment, so without it they are disabled (the + // server does not abort) and the loan keeps the pre-amendment computation. + // + // The test runs the same scenario under both amendment settings and checks + // the stored principal against a ground-truth value derived independently of + // the loan-state computation under test. + void + testBugOverpaymentPrincipalChange() + { + testcase("bug: doOverpayment asserts 'principal change agrees'"); + + using namespace jtx; + using namespace loan; + using namespace xrpl::detail; + + struct Params + { + TenthBips32 interestRate; + TenthBips16 managementFeeRate; + std::uint32_t paymentTotal; + std::uint32_t paymentInterval; + std::int64_t principal; + Number overpayment; + TenthBips32 overpaymentInterestRate; + TenthBips32 overpaymentFeeRate; + std::optional vaultScale; + }; + + struct Result + { + Number principalOutstanding; // stored principal after the LoanPay + Number expectedNewPrincipal; // ground truth, independent of the fix + Number managementFeeChange; // managementFeeOutstanding after - before + Number unit; // one scale-unit at the loan scale + }; + + auto runScenario = [this](FeatureBitset features, Params const& p) -> Result { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const lender{"vaultOwner"}; + Account const borrower{"borrower"}; + + env.fund(XRP(1'000'000), issuer, lender, borrower); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const iouAsset = issuer["USD"]; + Asset const asset = iouAsset.raw(); + STAmount const iouLimit{asset, Number{9'999'999'999'999'999LL}}; + env(trust(lender, iouLimit)); + env(trust(borrower, iouLimit)); + env(pay(issuer, lender, iouAsset(1'000'000))); + env(pay(issuer, borrower, iouAsset(1'000'000))); + env.close(); + + auto const broker = createVaultAndBroker( + env, + iouAsset, + lender, + {.vaultDeposit = 900'000, + .debtMax = 0, + .managementFeeRate = p.managementFeeRate, + .vaultScale = p.vaultScale}); + + auto const brokerSle = env.le(broker.brokerKeylet()); + BEAST_EXPECT(brokerSle); + auto const loanSequence = brokerSle ? brokerSle->at(sfLoanSequence) : 0; + auto const loanKeylet = keylet::loan(broker.brokerID, loanSequence); + + env(set(borrower, broker.brokerID, Number{p.principal}, tfLoanOverpayment), + Sig(sfCounterpartySignature, lender), + kInterestRate(p.interestRate), + kPaymentTotal(p.paymentTotal), + kPaymentInterval(p.paymentInterval), + kGracePeriod(p.paymentInterval), + kOverpaymentFee(p.overpaymentFeeRate), + kOverpaymentInterestRate(p.overpaymentInterestRate), + Fee(env.current()->fees().base * 2), + Ter(tesSUCCESS)); + env.close(); + + // The single LoanPay below makes one regular payment (the overpayment + // is smaller than one period) and leaves the residual as an + // overpayment. + auto const s = getCurrentState(env, broker, loanKeylet); + auto const periodicRate = loanPeriodicRate(s.interestRate, s.paymentInterval); + auto const onePeriod = computePaymentComponents( + env.current()->rules(), + asset, + s.loanScale, + s.totalValue, + s.principalOutstanding, + s.managementFeeOutstanding, + s.periodicPayment, + periodicRate, + s.paymentRemaining, + p.managementFeeRate); + + // Ground truth: the stored principal must drop by exactly the regular + // payment's principal portion plus the overpayment's principal + // portion. computeOverpaymentComponents depends only on the + // overpayment amount and rates (not on the loan-state computation + // under test), so it is an independent oracle. Both components are + // computed under the same rules as the env so the payment factor + // matches. + auto const overpaymentComponents = computeOverpaymentComponents( + env.current()->rules(), + asset, + s.loanScale, + p.overpayment, + p.overpaymentInterestRate, + p.overpaymentFeeRate, + p.managementFeeRate); + Number const expectedNewPrincipal = s.principalOutstanding - + onePeriod.trackedPrincipalDelta - overpaymentComponents.trackedPrincipalDelta; + + Number const managementFeeBefore = s.managementFeeOutstanding; + + STAmount const payAmount{asset, onePeriod.trackedValueDelta + p.overpayment}; + env(pay(borrower, loanKeylet.key, payAmount), + Txflags(tfLoanOverpayment), + Ter(tesSUCCESS)); + env.close(); + + auto const loanSle = env.le(loanKeylet); + BEAST_EXPECT(loanSle); + + return Result{ + .principalOutstanding = loanSle ? Number{loanSle->at(sfPrincipalOutstanding)} : 0, + .expectedNewPrincipal = expectedNewPrincipal, + .managementFeeChange = + (loanSle ? Number{loanSle->at(sfManagementFeeOutstanding)} : Number{0}) - + managementFeeBefore, + .unit = Number{1, s.loanScale}}; + }; + + // Scenario 1: the original near-zero-rate principal reproduction + // (loanScale -10, no management fee). 0.049999998 is smaller than one + // period, so it stays a residual overpayment. + Params const principalCase{ + .interestRate = TenthBips32{1}, + .managementFeeRate = TenthBips16{0}, + .paymentTotal = 3, + .paymentInterval = 60, + .principal = 100, + .overpayment = Number{49999998, -9}, + .overpaymentInterestRate = TenthBips32{1000}, + .overpaymentFeeRate = TenthBips32{1000}, + .vaultScale = 1}; + + // With fixCleanup3_2_0 the stored principal lands exactly on the + // ground-truth grid point: it is reduced by exactly the overpayment's + // principal portion. This is the key correctness check: if the principal + // pin were removed (even with the assertions still gated off), the lossy + // (P * factor) / factor round-trip would leave the principal one + // scale-unit high and this would fail. + Result const fixed = runScenario(all_, principalCase); + BEAST_EXPECTS( + fixed.principalOutstanding == fixed.expectedNewPrincipal, + "fixed principal " + to_string(fixed.principalOutstanding) + " != expected " + + to_string(fixed.expectedNewPrincipal)); + + // Without the amendment the loan amortizes with the catastrophically + // cancelling near-zero payment factor, so its schedule (and ground truth) + // differ from the fixed case; the gated assertions keep the server from + // aborting and the overpayment still lands exactly on that schedule. + Result const legacy = runScenario(all_ - fixCleanup3_2_0, principalCase); + BEAST_EXPECTS( + legacy.principalOutstanding == legacy.expectedNewPrincipal, + "legacy principal " + to_string(legacy.principalOutstanding) + " != expected " + + to_string(legacy.expectedNewPrincipal)); + + // Scenario 2: a normal-rate loan with a 10% management fee. At a normal + // rate the payment factor is identical across the amendment, so toggling + // fixCleanup3_2_0 isolates the fix. This overpayment (found by search) + // lands on a state where both the principal and the management fee differ + // by one scale-unit between the fixed and legacy paths. + Params const feeCase{ + .interestRate = TenthBips32{10000}, + .managementFeeRate = TenthBips16{10000}, + .paymentTotal = 6, + .paymentInterval = 30u * 24 * 60 * 60, + .principal = 1000, + .overpayment = Number{214367363, -10}, + .overpaymentInterestRate = TenthBips32{0}, + .overpaymentFeeRate = TenthBips32{0}, + .vaultScale = std::nullopt}; + + Result const feeFixed = runScenario(all_, feeCase); + Result const feeLegacy = runScenario(all_ - fixCleanup3_2_0, feeCase); + + // With the fix the principal is the exact reduction; without it the lossy + // (P * factor) / factor round-trip leaves it one scale-unit high. + BEAST_EXPECTS( + feeFixed.principalOutstanding == feeFixed.expectedNewPrincipal, + "fee-case fixed principal " + to_string(feeFixed.principalOutstanding) + + " != expected " + to_string(feeFixed.expectedNewPrincipal)); + BEAST_EXPECTS( + feeLegacy.principalOutstanding == feeLegacy.expectedNewPrincipal + feeLegacy.unit, + "fee-case legacy principal " + to_string(feeLegacy.principalOutstanding) + + " != expected " + to_string(feeLegacy.expectedNewPrincipal + feeLegacy.unit)); + + // Management fee: the overpayment re-amortizes a fee-bearing loan, so the management fee + // outstanding drops. + // + // Unlike the principal that is already at the correct precision, the re-amortized + // management fee is tenthBipsOfValue of the new schedule's gross interest, which depends + // on the recomputed periodic payment. So the expected change below is a pinned constant + // captured from a passing run a magic value only because there is nothing simpler to + // compare against. + // + // At the integration level, toggling the amendment also changes the regular payment's + // rounding so a fixed-vs-legacy comparison cannot isolate the overpayment management-fee + // fix. + BEAST_EXPECT(feeFixed.managementFeeChange == feeLegacy.managementFeeChange); + BEAST_EXPECTS( + (feeFixed.managementFeeChange == Number{-8219709543, -10}), + "fee-case mgmt fee change " + to_string(feeFixed.managementFeeChange)); + } + + // A LoanSet with InterestRate = 1 (0.001% annualized, the minimum non-zero + // rate). At such a near-zero rate the closed-form payment factor + // (1 + r)^n - 1 cancels catastrophically. + // + // Without fixCleanup3_2_0 the resulting amortization is degenerate and the + // LoanSet is rejected with tecPRECISION_LOSS (no loan created). With the + // amendment, computePowerMinusOneHybrid uses a numerically-stable series + // expansion, so the loan is created and the scheduled payments + // (2 * periodicPayment) cover the principal — no economic underpayment + // (yield theft). + // + // The test runs the same LoanSet under both amendment settings and pins the + // exact outcome for each. + void + testLoanSetNearZeroInterestRateSucceeds() + { + testcase("LoanSet near-zero interest rate covers principal"); + + using namespace jtx; + using namespace loan; + + Number const principalRequested{1000}; + + struct Result + { + TER ter = tesSUCCESS; + bool created = false; + std::int32_t loanScale = 0; + Number principal; + Number totalValue; + Number managementFee; + Number periodicPayment; + }; + + auto runScenario = [&](FeatureBitset features, TER expectedTer) -> Result { + Env env(*this, features); + + Account const issuer{"issuer"}; + Account const lender{"vaultOwner"}; + Account const borrower{"borrower"}; + + env.fund(XRP(1'000'000), issuer, lender, borrower); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const iouAsset = issuer["USD"]; + STAmount const iouLimit{iouAsset.raw(), Number{9'999'999'999'999'999LL}}; + env(trust(lender, iouLimit)); + env(trust(borrower, iouLimit)); + env(pay(issuer, lender, iouAsset(1'000'000))); + env(pay(issuer, borrower, iouAsset(1'000'000))); + env.close(); + + auto const broker = createVaultAndBroker( + env, + iouAsset, + lender, + {.vaultDeposit = 100'000, .debtMax = 0, .managementFeeRate = TenthBips16{0}}); + + auto const brokerSle = env.le(broker.brokerKeylet()); + BEAST_EXPECT(brokerSle); + auto const loanSequence = brokerSle ? brokerSle->at(sfLoanSequence) : 0; + auto const loanKeylet = keylet::loan(broker.brokerID, loanSequence); + + env(set(borrower, broker.brokerID, principalRequested), + Sig(sfCounterpartySignature, lender), + kInterestRate(TenthBips32{1}), + kPaymentTotal(2), + kPaymentInterval(400), + Fee(env.current()->fees().base * 2), + Ter(expectedTer)); + env.close(); + + Result r; + r.ter = env.ter(); + if (auto const loanSle = env.le(loanKeylet)) + { + r.created = true; + r.loanScale = loanSle->at(sfLoanScale); + r.principal = loanSle->at(sfPrincipalOutstanding); + r.totalValue = loanSle->at(sfTotalValueOutstanding); + r.managementFee = loanSle->at(sfManagementFeeOutstanding); + r.periodicPayment = loanSle->at(sfPeriodicPayment); + } + return r; + }; + + Result const fixed = runScenario(all_, tesSUCCESS); + Result const legacy = runScenario(all_ - fixCleanup3_2_0, tecPRECISION_LOSS); + + // Without the amendment, the catastrophically-cancelling closed-form + // payment factor produces a degenerate amortization that fails + // checkLoanGuards: the LoanSet is rejected with tecPRECISION_LOSS and no + // loan is created. + BEAST_EXPECT(legacy.ter == tecPRECISION_LOSS); + BEAST_EXPECT(!legacy.created); + + // With the amendment the stable series expansion produces a valid loan + // at loanScale -10. + BEAST_EXPECT(fixed.ter == tesSUCCESS); + BEAST_EXPECT(fixed.created); + BEAST_EXPECT(fixed.loanScale == -10); + BEAST_EXPECT(fixed.principal == principalRequested); + BEAST_EXPECT((fixed.totalValue == Number{10000000001903, -10})); + BEAST_EXPECT(fixed.managementFee == beast::kZero); + + // Periodic payment from the numerically-stable series expansion, and the + // scheduled total (2 * periodicPayment) which exceeds the 1000 principal + // — no economic underpayment / yield theft. + BEAST_EXPECT((fixed.periodicPayment == Number{5000000000951293762, -16})); + BEAST_EXPECT((fixed.periodicPayment * 2 == Number{1000000000190258752, -15})); + BEAST_EXPECT(fixed.periodicPayment * 2 > principalRequested); + } + // An overpayment whose residual amount has more precision than loanScale // fires the isRounded(asset, overpayment, loanScale) assertion in // computeOverpaymentComponents (and a downstream "interest paid agrees" @@ -8358,12 +8718,14 @@ protected: testLimitExceeded(); testLoanSetBlockedLoanPayAllowedWhenCanTransferCleared(); testLendingCanTradeClearedNoImpact(); + testBugOverpaymentPrincipalChange(); testBugOverpayUnroundedAmount(); for (auto const flags : {0u, tfLoanOverpayment}) testYieldTheftRounding(flags); testBugInterestDueDeltaCrash(); testFullLifecycleVaultPnLNearZeroRate(); + testLoanSetNearZeroInterestRateSucceeds(); } // Tests run under each entry in amendmentCombinations(). From 7e15621e7b79686da649f5bffb0b56d7eaa33d66 Mon Sep 17 00:00:00 2001 From: Bart Date: Sun, 31 May 2026 18:55:18 -0400 Subject: [PATCH 48/87] release: Bump version to 3.2.0-rc3 (#7371) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 2e33d03088..7de1862dfc 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -23,7 +23,7 @@ namespace { //------------------------------------------------------------------------------ // clang-format off // NOLINTNEXTLINE(readability-identifier-naming) -char const* const versionString = "3.2.0-rc2" +char const* const versionString = "3.2.0-rc3" // clang-format on ; From 0fffe23abc3a42e7d8016fbbd9a0beed3c40bbc9 Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Sun, 31 May 2026 20:33:19 -0700 Subject: [PATCH 49/87] fix: Adjust xrpld systemd service and update timer (#7374) --- package/debian/rules | 2 +- package/shared/update-xrpld.timer | 2 +- package/shared/xrpld.service | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package/debian/rules b/package/debian/rules index cd94da7e5b..612fe1b1a9 100644 --- a/package/debian/rules +++ b/package/debian/rules @@ -10,7 +10,7 @@ override_dh_auto_configure override_dh_auto_build override_dh_auto_test: override_dh_installsystemd: dh_installsystemd --no-stop-on-upgrade xrpld.service - dh_installsystemd --name=update-xrpld --no-start update-xrpld.service update-xrpld.timer + dh_installsystemd --name=update-xrpld --no-enable --no-start update-xrpld.service update-xrpld.timer execute_before_dh_installtmpfiles: dh_installsysusers diff --git a/package/shared/update-xrpld.timer b/package/shared/update-xrpld.timer index 21dabf1400..9fba09d30a 100644 --- a/package/shared/update-xrpld.timer +++ b/package/shared/update-xrpld.timer @@ -3,7 +3,7 @@ Description=Daily xrpld update check [Timer] OnCalendar=*-*-* 00:00:00 -RandomizedDelaySec=24h +RandomizedDelaySec=4h Persistent=true [Install] diff --git a/package/shared/xrpld.service b/package/shared/xrpld.service index 72b6cc9938..8e10ed2eee 100644 --- a/package/shared/xrpld.service +++ b/package/shared/xrpld.service @@ -2,14 +2,15 @@ Description=XRP Ledger Daemon After=network-online.target Wants=network-online.target -StartLimitIntervalSec=300 +StartLimitIntervalSec=5min StartLimitBurst=5 [Service] Type=simple ExecStart=/usr/bin/xrpld --net --silent --conf /etc/xrpld/xrpld.cfg -Restart=always +Restart=on-failure RestartSec=5s +TimeoutStopSec=5min NoNewPrivileges=true ProtectSystem=full ProtectHome=true From 109b64910616a84aad9b7a06514526431c8fa489 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:27:13 +0200 Subject: [PATCH 50/87] refactor: Use `STLedgerEntry` type aliases instead of `std::shared_ptr` (#7282) --- .../codegen/templates/LedgerEntry.h.mako | 4 +- include/xrpl/basics/TaggedCache.h | 2 +- include/xrpl/ledger/ApplyView.h | 20 ++--- include/xrpl/ledger/ApplyViewImpl.h | 4 +- include/xrpl/ledger/BookDirs.h | 6 +- include/xrpl/ledger/CachedView.h | 2 +- include/xrpl/ledger/Dir.h | 6 +- include/xrpl/ledger/Ledger.h | 10 +-- include/xrpl/ledger/OpenView.h | 8 +- include/xrpl/ledger/RawView.h | 6 +- include/xrpl/ledger/ReadView.h | 6 +- include/xrpl/ledger/View.h | 7 +- include/xrpl/ledger/detail/ApplyStateTable.h | 30 +++---- include/xrpl/ledger/detail/ApplyViewBase.h | 16 ++-- include/xrpl/ledger/detail/RawStateTable.h | 12 +-- include/xrpl/ledger/helpers/AMMHelpers.h | 4 +- .../xrpl/ledger/helpers/AccountRootHelpers.h | 13 +-- .../xrpl/ledger/helpers/CredentialHelpers.h | 4 +- include/xrpl/ledger/helpers/DelegateHelpers.h | 4 +- .../xrpl/ledger/helpers/DirectoryHelpers.h | 22 ++--- include/xrpl/ledger/helpers/EscrowHelpers.h | 6 +- include/xrpl/ledger/helpers/NFTokenHelpers.h | 13 +-- include/xrpl/ledger/helpers/OfferHelpers.h | 4 +- .../ledger/helpers/PaymentChannelHelpers.h | 6 +- .../xrpl/ledger/helpers/RippleStateHelpers.h | 6 +- include/xrpl/ledger/helpers/VaultHelpers.h | 24 ++---- .../xrpl/protocol_autogen/LedgerEntryBase.h | 6 +- .../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 +- .../protocol_autogen/ledger_entries/Ticket.h | 4 +- .../protocol_autogen/ledger_entries/Vault.h | 4 +- .../ledger_entries/XChainOwnedClaimID.h | 4 +- .../XChainOwnedCreateAccountClaimID.h | 4 +- include/xrpl/tx/ApplyContext.h | 4 +- include/xrpl/tx/Transactor.h | 7 +- include/xrpl/tx/invariants/AMMInvariant.h | 2 +- include/xrpl/tx/invariants/FreezeInvariant.h | 19 ++-- include/xrpl/tx/invariants/InvariantCheck.h | 35 ++++---- .../xrpl/tx/invariants/LoanBrokerInvariant.h | 2 +- include/xrpl/tx/invariants/LoanInvariant.h | 2 +- include/xrpl/tx/invariants/MPTInvariant.h | 4 +- include/xrpl/tx/invariants/NFTInvariant.h | 4 +- .../tx/invariants/PermissionedDEXInvariant.h | 2 +- .../invariants/PermissionedDomainInvariant.h | 2 +- include/xrpl/tx/invariants/VaultInvariant.h | 2 +- include/xrpl/tx/paths/BookTip.h | 2 +- .../tx/transactors/account/AccountDelete.h | 5 +- .../xrpl/tx/transactors/account/AccountSet.h | 5 +- .../tx/transactors/account/SetRegularKey.h | 5 +- .../tx/transactors/account/SignerListSet.h | 5 +- .../xrpl/tx/transactors/bridge/XChainBridge.h | 40 ++------- .../xrpl/tx/transactors/check/CheckCancel.h | 5 +- include/xrpl/tx/transactors/check/CheckCash.h | 5 +- .../xrpl/tx/transactors/check/CheckCreate.h | 5 +- .../credentials/CredentialAccept.h | 5 +- .../credentials/CredentialCreate.h | 5 +- .../credentials/CredentialDelete.h | 5 +- .../tx/transactors/delegate/DelegateSet.h | 7 +- include/xrpl/tx/transactors/dex/AMMBid.h | 5 +- include/xrpl/tx/transactors/dex/AMMClawback.h | 5 +- include/xrpl/tx/transactors/dex/AMMCreate.h | 5 +- include/xrpl/tx/transactors/dex/AMMDelete.h | 5 +- include/xrpl/tx/transactors/dex/AMMDeposit.h | 5 +- include/xrpl/tx/transactors/dex/AMMVote.h | 5 +- include/xrpl/tx/transactors/dex/AMMWithdraw.h | 7 +- include/xrpl/tx/transactors/dex/OfferCancel.h | 5 +- include/xrpl/tx/transactors/dex/OfferCreate.h | 7 +- include/xrpl/tx/transactors/did/DIDDelete.h | 7 +- include/xrpl/tx/transactors/did/DIDSet.h | 5 +- .../xrpl/tx/transactors/escrow/EscrowCancel.h | 5 +- .../xrpl/tx/transactors/escrow/EscrowCreate.h | 5 +- .../xrpl/tx/transactors/escrow/EscrowFinish.h | 5 +- .../lending/LoanBrokerCoverClawback.h | 5 +- .../lending/LoanBrokerCoverDeposit.h | 5 +- .../lending/LoanBrokerCoverWithdraw.h | 5 +- .../tx/transactors/lending/LoanBrokerDelete.h | 5 +- .../tx/transactors/lending/LoanBrokerSet.h | 5 +- .../xrpl/tx/transactors/lending/LoanDelete.h | 5 +- .../xrpl/tx/transactors/lending/LoanManage.h | 5 +- include/xrpl/tx/transactors/lending/LoanPay.h | 5 +- include/xrpl/tx/transactors/lending/LoanSet.h | 5 +- .../tx/transactors/nft/NFTokenAcceptOffer.h | 9 +- include/xrpl/tx/transactors/nft/NFTokenBurn.h | 5 +- .../tx/transactors/nft/NFTokenCancelOffer.h | 5 +- .../tx/transactors/nft/NFTokenCreateOffer.h | 5 +- include/xrpl/tx/transactors/nft/NFTokenMint.h | 5 +- .../xrpl/tx/transactors/nft/NFTokenModify.h | 5 +- .../xrpl/tx/transactors/oracle/OracleDelete.h | 11 +-- .../xrpl/tx/transactors/oracle/OracleSet.h | 5 +- .../tx/transactors/payment/DepositPreauth.h | 5 +- include/xrpl/tx/transactors/payment/Payment.h | 5 +- .../payment_channel/PaymentChannelClaim.h | 5 +- .../payment_channel/PaymentChannelCreate.h | 5 +- .../payment_channel/PaymentChannelFund.h | 5 +- .../PermissionedDomainDelete.h | 5 +- .../PermissionedDomainSet.h | 5 +- include/xrpl/tx/transactors/system/Batch.h | 5 +- include/xrpl/tx/transactors/system/Change.h | 5 +- .../tx/transactors/system/LedgerStateFix.h | 5 +- .../xrpl/tx/transactors/system/TicketCreate.h | 5 +- include/xrpl/tx/transactors/token/Clawback.h | 5 +- .../tx/transactors/token/MPTokenAuthorize.h | 5 +- .../transactors/token/MPTokenIssuanceCreate.h | 5 +- .../token/MPTokenIssuanceDestroy.h | 5 +- .../tx/transactors/token/MPTokenIssuanceSet.h | 5 +- include/xrpl/tx/transactors/token/TrustSet.h | 5 +- .../xrpl/tx/transactors/vault/VaultClawback.h | 9 +- .../xrpl/tx/transactors/vault/VaultCreate.h | 5 +- .../xrpl/tx/transactors/vault/VaultDelete.h | 5 +- .../xrpl/tx/transactors/vault/VaultDeposit.h | 5 +- include/xrpl/tx/transactors/vault/VaultSet.h | 5 +- .../xrpl/tx/transactors/vault/VaultWithdraw.h | 5 +- src/libxrpl/ledger/ApplyStateTable.cpp | 28 +++--- src/libxrpl/ledger/ApplyView.cpp | 6 +- src/libxrpl/ledger/ApplyViewBase.cpp | 16 ++-- src/libxrpl/ledger/ApplyViewImpl.cpp | 9 +- src/libxrpl/ledger/CachedView.cpp | 3 +- src/libxrpl/ledger/Ledger.cpp | 10 +-- src/libxrpl/ledger/OpenView.cpp | 8 +- src/libxrpl/ledger/RawStateTable.cpp | 14 +-- src/libxrpl/ledger/View.cpp | 9 +- src/libxrpl/ledger/helpers/AMMHelpers.cpp | 9 +- .../ledger/helpers/AccountRootHelpers.cpp | 12 +-- .../ledger/helpers/CredentialHelpers.cpp | 7 +- .../ledger/helpers/DirectoryHelpers.cpp | 19 ++-- src/libxrpl/ledger/helpers/NFTokenHelpers.cpp | 36 ++++---- src/libxrpl/ledger/helpers/OfferHelpers.cpp | 4 +- .../ledger/helpers/PaymentChannelHelpers.cpp | 8 +- .../ledger/helpers/RippleStateHelpers.cpp | 6 +- src/libxrpl/ledger/helpers/VaultHelpers.cpp | 19 ++-- src/libxrpl/tx/ApplyContext.cpp | 19 ++-- src/libxrpl/tx/Transactor.cpp | 9 +- src/libxrpl/tx/invariants/AMMInvariant.cpp | 6 +- src/libxrpl/tx/invariants/FreezeInvariant.cpp | 22 ++--- src/libxrpl/tx/invariants/InvariantCheck.cpp | 70 +++------------ .../tx/invariants/LoanBrokerInvariant.cpp | 7 +- src/libxrpl/tx/invariants/LoanInvariant.cpp | 7 +- src/libxrpl/tx/invariants/MPTInvariant.cpp | 11 +-- src/libxrpl/tx/invariants/NFTInvariant.cpp | 13 +-- .../invariants/PermissionedDEXInvariant.cpp | 7 +- .../PermissionedDomainInvariant.cpp | 8 +- src/libxrpl/tx/invariants/VaultInvariant.cpp | 6 +- src/libxrpl/tx/paths/BookTip.cpp | 4 +- src/libxrpl/tx/paths/DirectStep.cpp | 8 +- src/libxrpl/tx/paths/MPTEndpointStep.cpp | 9 +- src/libxrpl/tx/paths/OfferStream.cpp | 3 +- .../tx/transactors/account/AccountDelete.cpp | 30 +++---- .../tx/transactors/account/AccountSet.cpp | 6 +- .../tx/transactors/account/SetRegularKey.cpp | 7 +- .../tx/transactors/account/SignerListSet.cpp | 5 +- .../tx/transactors/bridge/XChainBridge.cpp | 48 +++-------- .../tx/transactors/check/CheckCancel.cpp | 7 +- .../tx/transactors/check/CheckCash.cpp | 8 +- .../tx/transactors/check/CheckCreate.cpp | 5 +- .../credentials/CredentialAccept.cpp | 7 +- .../credentials/CredentialCreate.cpp | 5 +- .../credentials/CredentialDelete.cpp | 7 +- .../tx/transactors/delegate/DelegateSet.cpp | 7 +- .../tx/transactors/delegate/DelegateUtils.cpp | 5 +- src/libxrpl/tx/transactors/dex/AMMBid.cpp | 6 +- .../tx/transactors/dex/AMMClawback.cpp | 6 +- src/libxrpl/tx/transactors/dex/AMMCreate.cpp | 5 +- src/libxrpl/tx/transactors/dex/AMMDelete.cpp | 7 +- src/libxrpl/tx/transactors/dex/AMMDeposit.cpp | 6 +- src/libxrpl/tx/transactors/dex/AMMVote.cpp | 6 +- .../tx/transactors/dex/AMMWithdraw.cpp | 8 +- .../tx/transactors/dex/OfferCancel.cpp | 7 +- .../tx/transactors/dex/OfferCreate.cpp | 7 +- src/libxrpl/tx/transactors/did/DIDDelete.cpp | 13 +-- src/libxrpl/tx/transactors/did/DIDSet.cpp | 7 +- .../tx/transactors/escrow/EscrowCancel.cpp | 6 +- .../tx/transactors/escrow/EscrowCreate.cpp | 5 +- .../tx/transactors/escrow/EscrowFinish.cpp | 6 +- .../lending/LoanBrokerCoverClawback.cpp | 6 +- .../lending/LoanBrokerCoverDeposit.cpp | 7 +- .../lending/LoanBrokerCoverWithdraw.cpp | 7 +- .../transactors/lending/LoanBrokerDelete.cpp | 7 +- .../tx/transactors/lending/LoanBrokerSet.cpp | 5 +- .../tx/transactors/lending/LoanDelete.cpp | 7 +- .../tx/transactors/lending/LoanManage.cpp | 7 +- .../tx/transactors/lending/LoanPay.cpp | 6 +- .../tx/transactors/lending/LoanSet.cpp | 5 +- .../tx/transactors/nft/NFTokenAcceptOffer.cpp | 15 ++-- .../tx/transactors/nft/NFTokenBurn.cpp | 7 +- .../tx/transactors/nft/NFTokenCancelOffer.cpp | 7 +- .../tx/transactors/nft/NFTokenCreateOffer.cpp | 7 +- .../tx/transactors/nft/NFTokenMint.cpp | 6 +- .../tx/transactors/nft/NFTokenModify.cpp | 7 +- .../tx/transactors/oracle/OracleDelete.cpp | 9 +- .../tx/transactors/oracle/OracleSet.cpp | 5 +- .../tx/transactors/payment/DepositPreauth.cpp | 5 +- .../tx/transactors/payment/Payment.cpp | 5 +- .../payment_channel/PaymentChannelClaim.cpp | 6 +- .../payment_channel/PaymentChannelCreate.cpp | 5 +- .../payment_channel/PaymentChannelFund.cpp | 7 +- .../PermissionedDomainDelete.cpp | 7 +- .../PermissionedDomainSet.cpp | 5 +- src/libxrpl/tx/transactors/system/Batch.cpp | 6 +- src/libxrpl/tx/transactors/system/Change.cpp | 5 +- .../tx/transactors/system/LedgerStateFix.cpp | 6 +- .../tx/transactors/system/TicketCreate.cpp | 5 +- src/libxrpl/tx/transactors/token/Clawback.cpp | 6 +- .../tx/transactors/token/MPTokenAuthorize.cpp | 9 +- .../token/MPTokenIssuanceCreate.cpp | 5 +- .../token/MPTokenIssuanceDestroy.cpp | 7 +- .../transactors/token/MPTokenIssuanceSet.cpp | 8 +- src/libxrpl/tx/transactors/token/TrustSet.cpp | 6 +- .../tx/transactors/vault/VaultClawback.cpp | 12 +-- .../tx/transactors/vault/VaultCreate.cpp | 5 +- .../tx/transactors/vault/VaultDelete.cpp | 7 +- .../tx/transactors/vault/VaultDeposit.cpp | 6 +- src/libxrpl/tx/transactors/vault/VaultSet.cpp | 7 +- .../tx/transactors/vault/VaultWithdraw.cpp | 6 +- src/test/app/CheckMPT_test.cpp | 7 +- src/test/app/Check_test.cpp | 7 +- src/test/app/FlowMPT_test.cpp | 51 ++++++----- src/test/app/Flow_test.cpp | 9 +- src/test/app/Invariants_test.cpp | 2 +- src/test/app/OfferMPT_test.cpp | 45 ++++------ src/test/app/Offer_test.cpp | 37 ++++---- src/test/app/PayChan_test.cpp | 10 +-- src/test/app/XChain_test.cpp | 10 +-- src/test/jtx/Env.h | 4 +- src/test/jtx/PathSet.h | 4 +- src/test/jtx/TestHelpers.h | 2 +- src/test/jtx/impl/Env.cpp | 4 +- src/test/jtx/impl/TestHelpers.cpp | 2 +- src/test/jtx/impl/owners.cpp | 4 +- src/test/ledger/View_test.cpp | 4 +- src/xrpld/app/misc/NetworkOPs.cpp | 2 +- src/xrpld/app/misc/TxQ.h | 8 +- src/xrpld/app/misc/detail/TxQ.cpp | 12 ++- src/xrpld/consensus/DisputedTx.h | 2 +- src/xrpld/rpc/detail/AssetCache.cpp | 2 +- src/xrpld/rpc/detail/RPCHelpers.cpp | 8 +- src/xrpld/rpc/detail/RPCHelpers.h | 7 +- src/xrpld/rpc/detail/TransactionSign.cpp | 9 +- src/xrpld/rpc/detail/TrustLine.cpp | 22 ++--- src/xrpld/rpc/detail/TrustLine.h | 8 +- .../rpc/handlers/account/AccountChannels.cpp | 5 +- .../rpc/handlers/account/AccountLines.cpp | 3 +- .../rpc/handlers/account/AccountOffers.cpp | 7 +- .../rpc/handlers/account/GatewayBalances.cpp | 2 +- .../rpc/handlers/account/NoRippleCheck.cpp | 86 +++++++++---------- src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp | 2 +- .../handlers/orderbook/DepositAuthorized.cpp | 4 +- .../handlers/orderbook/GetAggregatePrice.cpp | 2 +- .../rpc/handlers/orderbook/NFTOffersHelpers.h | 14 +-- .../rpc/handlers/transaction/Simulate.cpp | 2 +- 273 files changed, 719 insertions(+), 1475 deletions(-) diff --git a/cmake/scripts/codegen/templates/LedgerEntry.h.mako b/cmake/scripts/codegen/templates/LedgerEntry.h.mako index 31029cd311..63f5f39ef9 100644 --- a/cmake/scripts/codegen/templates/LedgerEntry.h.mako +++ b/cmake/scripts/codegen/templates/LedgerEntry.h.mako @@ -33,7 +33,7 @@ public: * @brief Construct a ${name} ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit ${name}(std::shared_ptr sle) + explicit ${name}(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -168,7 +168,7 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - ${name}Builder(std::shared_ptr sle) + ${name}Builder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ${tag}) { diff --git a/include/xrpl/basics/TaggedCache.h b/include/xrpl/basics/TaggedCache.h index fca9eabde2..380b7c687f 100644 --- a/include/xrpl/basics/TaggedCache.h +++ b/include/xrpl/basics/TaggedCache.h @@ -157,7 +157,7 @@ public: /** Fetch an item from the cache. If the digest was not found, Handler will be called with this signature: - std::shared_ptr(void) + SLE::const_pointer(void) */ template SharedPointerType diff --git a/include/xrpl/ledger/ApplyView.h b/include/xrpl/ledger/ApplyView.h index f825311e1d..362eae0f79 100644 --- a/include/xrpl/ledger/ApplyView.h +++ b/include/xrpl/ledger/ApplyView.h @@ -123,7 +123,7 @@ private: bool preserveOrder, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe); + std::function const& describe); public: ApplyView() = default; @@ -153,7 +153,7 @@ public: @return `nullptr` if the key is not present */ - virtual std::shared_ptr + virtual SLE::pointer peek(Keylet const& k) = 0; /** Remove a peeked SLE. @@ -168,7 +168,7 @@ public: The key is no longer associated with the SLE. */ virtual void - erase(std::shared_ptr const& sle) = 0; + erase(SLE::ref sle) = 0; /** Insert a new state SLE @@ -189,7 +189,7 @@ public: @note The key is taken from the SLE */ virtual void - insert(std::shared_ptr const& sle) = 0; + insert(SLE::ref sle) = 0; /** Indicate changes to a peeked SLE @@ -208,7 +208,7 @@ public: */ /** @{ */ virtual void - update(std::shared_ptr const& sle) = 0; + update(SLE::ref sle) = 0; //-------------------------------------------------------------------------- @@ -301,7 +301,7 @@ public: dirAppend( Keylet const& directory, Keylet const& key, - std::function const&)> const& describe) + std::function const& describe) { if (key.type != ltOFFER) { @@ -340,7 +340,7 @@ public: dirInsert( Keylet const& directory, uint256 const& key, - std::function const&)> const& describe) + std::function const& describe) { return dirAdd(false, directory, key, describe); } @@ -349,7 +349,7 @@ public: dirInsert( Keylet const& directory, Keylet const& key, - std::function const&)> const& describe) + std::function const& describe) { return dirAdd(false, directory, key.key, describe); } @@ -411,7 +411,7 @@ createRoot( ApplyView& view, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe); + std::function const& describe); auto findPreviousPage(ApplyView& view, Keylet const& directory, SLE::ref start); @@ -434,7 +434,7 @@ insertPage( SLE::ref next, uint256 const& key, Keylet const& directory, - std::function const&)> const& describe); + std::function const& describe); } // namespace directory } // namespace xrpl diff --git a/include/xrpl/ledger/ApplyViewImpl.h b/include/xrpl/ledger/ApplyViewImpl.h index 7f790f2be5..1245568630 100644 --- a/include/xrpl/ledger/ApplyViewImpl.h +++ b/include/xrpl/ledger/ApplyViewImpl.h @@ -67,8 +67,8 @@ public: std::function const& before, - std::shared_ptr const& after)> const& func); + SLE::const_ref before, + SLE::const_ref after)> const& func); private: std::optional deliver_; diff --git a/include/xrpl/ledger/BookDirs.h b/include/xrpl/ledger/BookDirs.h index 7eaede14ec..36798934da 100644 --- a/include/xrpl/ledger/BookDirs.h +++ b/include/xrpl/ledger/BookDirs.h @@ -11,13 +11,13 @@ private: uint256 const root_; uint256 const nextQuality_; uint256 const key_; - std::shared_ptr sle_ = nullptr; + SLE::const_pointer sle_ = nullptr; unsigned int entry_ = 0; uint256 index_; public: class const_iterator; // NOLINT(readability-identifier-naming) - using value_type = std::shared_ptr; + using value_type = SLE::const_pointer; BookDirs(ReadView const&, Book const&); @@ -76,7 +76,7 @@ private: uint256 nextQuality_; uint256 key_; uint256 curKey_; - std::shared_ptr sle_; + SLE::const_pointer sle_; unsigned int entry_ = 0; uint256 index_; std::optional mutable cache_; diff --git a/include/xrpl/ledger/CachedView.h b/include/xrpl/ledger/CachedView.h index 34a75e4c07..462db48ee3 100644 --- a/include/xrpl/ledger/CachedView.h +++ b/include/xrpl/ledger/CachedView.h @@ -36,7 +36,7 @@ public: bool exists(Keylet const& k) const override; - std::shared_ptr + SLE::const_pointer read(Keylet const& k) const override; bool diff --git a/include/xrpl/ledger/Dir.h b/include/xrpl/ledger/Dir.h index cfbef357b1..d305e21938 100644 --- a/include/xrpl/ledger/Dir.h +++ b/include/xrpl/ledger/Dir.h @@ -22,12 +22,12 @@ class Dir private: ReadView const* view_ = nullptr; Keylet root_; - std::shared_ptr sle_; + SLE::const_pointer sle_; STVector256 const* indexes_ = nullptr; public: class ConstIterator; - using value_type = std::shared_ptr; + using value_type = SLE::const_pointer; Dir(ReadView const&, Keylet const&); @@ -102,7 +102,7 @@ private: Keylet page_; uint256 index_; std::optional mutable cache_; - std::shared_ptr sle_; + SLE::const_pointer sle_; STVector256 const* indexes_ = nullptr; std::vector::const_iterator it_; }; diff --git a/include/xrpl/ledger/Ledger.h b/include/xrpl/ledger/Ledger.h index 351f7d80e5..5f7d79c61d 100644 --- a/include/xrpl/ledger/Ledger.h +++ b/include/xrpl/ledger/Ledger.h @@ -166,7 +166,7 @@ public: std::optional succ(uint256 const& key, std::optional const& last = std::nullopt) const override; - std::shared_ptr + SLE::const_pointer read(Keylet const& k) const override; std::unique_ptr @@ -202,16 +202,16 @@ public: // void - rawErase(std::shared_ptr const& sle) override; + rawErase(SLE::ref sle) override; void - rawInsert(std::shared_ptr const& sle) override; + rawInsert(SLE::ref sle) override; void rawErase(uint256 const& key); void - rawReplace(std::shared_ptr const& sle) override; + rawReplace(SLE::ref sle) override; void rawDestroyXRP(XRPAmount const& fee) override @@ -361,7 +361,7 @@ public: bool isVotingLedger() const; - std::shared_ptr + SLE::pointer peek(Keylet const& k) const; private: diff --git a/include/xrpl/ledger/OpenView.h b/include/xrpl/ledger/OpenView.h index 18d1a9399c..4ba2a7759b 100644 --- a/include/xrpl/ledger/OpenView.h +++ b/include/xrpl/ledger/OpenView.h @@ -197,7 +197,7 @@ public: std::optional succ(key_type const& key, std::optional const& last = std::nullopt) const override; - std::shared_ptr + SLE::const_pointer read(Keylet const& k) const override; std::unique_ptr @@ -224,13 +224,13 @@ public: // RawView void - rawErase(std::shared_ptr const& sle) override; + rawErase(SLE::ref sle) override; void - rawInsert(std::shared_ptr const& sle) override; + rawInsert(SLE::ref sle) override; void - rawReplace(std::shared_ptr const& sle) override; + rawReplace(SLE::ref sle) override; void rawDestroyXRP(XRPAmount const& fee) override; diff --git a/include/xrpl/ledger/RawView.h b/include/xrpl/ledger/RawView.h index cfcf807e13..cf61c3e814 100644 --- a/include/xrpl/ledger/RawView.h +++ b/include/xrpl/ledger/RawView.h @@ -25,7 +25,7 @@ public: can calculate metadata. */ virtual void - rawErase(std::shared_ptr const& sle) = 0; + rawErase(SLE::ref sle) = 0; /** Unconditionally insert a state item. @@ -39,7 +39,7 @@ public: @note The key is taken from the SLE */ virtual void - rawInsert(std::shared_ptr const& sle) = 0; + rawInsert(SLE::ref sle) = 0; /** Unconditionally replace a state item. @@ -54,7 +54,7 @@ public: @note The key is taken from the SLE */ virtual void - rawReplace(std::shared_ptr const& sle) = 0; + rawReplace(SLE::ref sle) = 0; /** Destroy XRP. diff --git a/include/xrpl/ledger/ReadView.h b/include/xrpl/ledger/ReadView.h index 4f9bf9c31d..f4ee7e6fd2 100644 --- a/include/xrpl/ledger/ReadView.h +++ b/include/xrpl/ledger/ReadView.h @@ -34,9 +34,9 @@ public: using key_type = uint256; - using mapped_type = std::shared_ptr; + using mapped_type = SLE::const_pointer; - struct SlesType : detail::ReadViewFwdRange> + struct SlesType : detail::ReadViewFwdRange { explicit SlesType(ReadView const& view); [[nodiscard]] Iterator @@ -143,7 +143,7 @@ public: @return `nullptr` if the key is not present or if the type does not match. */ - [[nodiscard]] virtual std::shared_ptr + [[nodiscard]] virtual SLE::const_pointer read(Keylet const& k) const = 0; // Accounts in a payment are not allowed to use assets acquired during that diff --git a/include/xrpl/ledger/View.h b/include/xrpl/ledger/View.h index 0d76c98a73..255413e459 100644 --- a/include/xrpl/ledger/View.h +++ b/include/xrpl/ledger/View.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -135,7 +134,7 @@ areCompatible( dirLink( ApplyView& view, AccountID const& owner, - std::shared_ptr& object, + SLE::pointer& object, SF_UINT64 const& node = sfOwnerNode); /** Checks that can withdraw funds from an object to itself or a destination. @@ -215,8 +214,8 @@ doWithdraw( * (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< - std::pair(LedgerEntryType, uint256 const&, std::shared_ptr&)>; +using EntryDeleter = + std::function(LedgerEntryType, uint256 const&, SLE::pointer&)>; /** 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 diff --git a/include/xrpl/ledger/detail/ApplyStateTable.h b/include/xrpl/ledger/detail/ApplyStateTable.h index 7b18f742b4..f40e3d0d1c 100644 --- a/include/xrpl/ledger/detail/ApplyStateTable.h +++ b/include/xrpl/ledger/detail/ApplyStateTable.h @@ -8,8 +8,6 @@ #include #include -#include - namespace xrpl::detail { // Helper class that buffers modifications @@ -26,7 +24,7 @@ private: Modify, }; - using items_t = std::map>>; + using items_t = std::map>; items_t items_; XRPAmount dropsDestroyed_{0}; @@ -60,10 +58,10 @@ public: [[nodiscard]] std::optional succ(ReadView const& base, key_type const& key, std::optional const& last) const; - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer read(ReadView const& base, Keylet const& k) const; - std::shared_ptr + SLE::pointer peek(ReadView const& base, Keylet const& k); [[nodiscard]] std::size_t @@ -75,23 +73,23 @@ public: std::function const& before, - std::shared_ptr const& after)> const& func) const; + SLE::const_ref before, + SLE::const_ref after)> const& func) const; void - erase(ReadView const& base, std::shared_ptr const& sle); + erase(ReadView const& base, SLE::ref sle); void - rawErase(ReadView const& base, std::shared_ptr const& sle); + rawErase(ReadView const& base, SLE::ref sle); void - insert(ReadView const& base, std::shared_ptr const& sle); + insert(ReadView const& base, SLE::ref sle); void - update(ReadView const& base, std::shared_ptr const& sle); + update(ReadView const& base, SLE::ref sle); void - replace(ReadView const& base, std::shared_ptr const& sle); + replace(ReadView const& base, SLE::ref sle); void destroyXRP(XRPAmount const& fee); @@ -104,12 +102,12 @@ public: } private: - using Mods = hash_map>; + using Mods = hash_map; static void - threadItem(TxMeta& meta, std::shared_ptr const& to); + threadItem(TxMeta& meta, SLE::ref to); - std::shared_ptr + SLE::pointer getForMod(ReadView const& base, key_type const& key, Mods& mods, beast::Journal j); void @@ -119,7 +117,7 @@ private: threadOwners( ReadView const& base, TxMeta& meta, - std::shared_ptr const& sle, + SLE::const_ref sle, Mods& mods, beast::Journal j); }; diff --git a/include/xrpl/ledger/detail/ApplyViewBase.h b/include/xrpl/ledger/detail/ApplyViewBase.h index 558c9e5d4d..d6493c46a8 100644 --- a/include/xrpl/ledger/detail/ApplyViewBase.h +++ b/include/xrpl/ledger/detail/ApplyViewBase.h @@ -40,7 +40,7 @@ public: [[nodiscard]] std::optional succ(key_type const& key, std::optional const& last = std::nullopt) const override; - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer read(Keylet const& k) const override; [[nodiscard]] std::unique_ptr @@ -69,28 +69,28 @@ public: [[nodiscard]] ApplyFlags flags() const override; - std::shared_ptr + SLE::pointer peek(Keylet const& k) override; void - erase(std::shared_ptr const& sle) override; + erase(SLE::ref sle) override; void - insert(std::shared_ptr const& sle) override; + insert(SLE::ref sle) override; void - update(std::shared_ptr const& sle) override; + update(SLE::ref sle) override; // RawView void - rawErase(std::shared_ptr const& sle) override; + rawErase(SLE::ref sle) override; void - rawInsert(std::shared_ptr const& sle) override; + rawInsert(SLE::ref sle) override; void - rawReplace(std::shared_ptr const& sle) override; + rawReplace(SLE::ref sle) override; void rawDestroyXRP(XRPAmount const& feeDrops) override; diff --git a/include/xrpl/ledger/detail/RawStateTable.h b/include/xrpl/ledger/detail/RawStateTable.h index e4329bf6fc..d2567e34f1 100644 --- a/include/xrpl/ledger/detail/RawStateTable.h +++ b/include/xrpl/ledger/detail/RawStateTable.h @@ -49,15 +49,15 @@ public: succ(ReadView const& base, key_type const& key, std::optional const& last) const; void - erase(std::shared_ptr const& sle); + erase(SLE::ref sle); void - insert(std::shared_ptr const& sle); + insert(SLE::ref sle); void - replace(std::shared_ptr const& sle); + replace(SLE::ref sle); - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer read(ReadView const& base, Keylet const& k) const; void @@ -84,10 +84,10 @@ private: struct SleAction { Action action; - std::shared_ptr sle; + SLE::pointer sle; // Constructor needed for emplacement in std::map - SleAction(Action action, std::shared_ptr const& sle) : action(action), sle(sle) + SleAction(Action action, SLE::pointer sle) : action(action), sle(std::move(sle)) { } }; diff --git a/include/xrpl/ledger/helpers/AMMHelpers.h b/include/xrpl/ledger/helpers/AMMHelpers.h index a146ef753b..61d6e9d2fb 100644 --- a/include/xrpl/ledger/helpers/AMMHelpers.h +++ b/include/xrpl/ledger/helpers/AMMHelpers.h @@ -792,7 +792,7 @@ deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast:: void initializeFeeAuctionVote( ApplyView& view, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account, Asset const& lptAsset, std::uint16_t tfee); @@ -812,7 +812,7 @@ Expected verifyAndAdjustLPTokenBalance( Sandbox& sb, STAmount const& lpTokens, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index 353c27fe41..c02cad98d8 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -36,11 +35,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, /** Adjust the owner count up or down. */ void -adjustOwnerCount( - ApplyView& view, - std::shared_ptr const& sle, - std::int32_t amount, - beast::Journal j); +adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Journal j); /** Returns IOU issuer transfer fee as Rate. Rate specifies * the fee as fractions of 1 billion. For example, 1% transfer rate @@ -76,9 +71,7 @@ getPseudoAccountFields(); - null pointer */ [[nodiscard]] bool -isPseudoAccount( - std::shared_ptr sleAcct, - std::set const& pseudoFieldFilter = {}); +isPseudoAccount(SLE::const_pointer sleAcct, std::set const& pseudoFieldFilter = {}); /** Convenience overload that reads the account from the view. */ [[nodiscard]] inline bool @@ -98,7 +91,7 @@ isPseudoAccount( * before using a field. The amendment check is **not** performed in * createPseudoAccount. */ -[[nodiscard]] Expected, TER> +[[nodiscard]] Expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); /** Checks the destination and tag. diff --git a/include/xrpl/ledger/helpers/CredentialHelpers.h b/include/xrpl/ledger/helpers/CredentialHelpers.h index e06d225934..549644764f 100644 --- a/include/xrpl/ledger/helpers/CredentialHelpers.h +++ b/include/xrpl/ledger/helpers/CredentialHelpers.h @@ -23,7 +23,7 @@ checkExpired(SLE const& sleCredential, NetClock::time_point const& closed); // Actually remove a credentials object from the ledger [[nodiscard]] TER -deleteSLE(ApplyView& view, std::shared_ptr const& sleCredential, beast::Journal j); +deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j); // Amendment and parameters checks for sfCredentialIDs field NotTEC @@ -70,7 +70,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + SLE::const_ref sleDst, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/DelegateHelpers.h b/include/xrpl/ledger/helpers/DelegateHelpers.h index 78ccc46d0b..9cdad7173d 100644 --- a/include/xrpl/ledger/helpers/DelegateHelpers.h +++ b/include/xrpl/ledger/helpers/DelegateHelpers.h @@ -15,7 +15,7 @@ namespace xrpl { * if not. */ NotTEC -checkTxPermission(std::shared_ptr const& delegate, STTx const& tx); +checkTxPermission(SLE::const_ref delegate, STTx const& tx); /** * Load the granular permissions granted to the delegate account for the @@ -28,7 +28,7 @@ checkTxPermission(std::shared_ptr const& delegate, STTx const& tx); */ void loadGranularPermission( - std::shared_ptr const& delegate, + SLE::const_ref delegate, TxType const& type, std::unordered_set& granularPermissions); diff --git a/include/xrpl/ledger/helpers/DirectoryHelpers.h b/include/xrpl/ledger/helpers/DirectoryHelpers.h index 2ae188182d..a0be52df99 100644 --- a/include/xrpl/ledger/helpers/DirectoryHelpers.h +++ b/include/xrpl/ledger/helpers/DirectoryHelpers.h @@ -115,7 +115,7 @@ bool cdirFirst( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry); @@ -123,7 +123,7 @@ bool dirFirst( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry); /** @} */ @@ -147,7 +147,7 @@ bool cdirNext( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry); @@ -155,17 +155,14 @@ bool dirNext( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry); /** @} */ /** Iterate all items in the given directory. */ void -forEachItem( - ReadView const& view, - Keylet const& root, - std::function const&)> const& f); +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 @@ -180,14 +177,11 @@ forEachItemAfter( uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)> const& f); + std::function const& f); /** Iterate all items in an account's owner directory. */ inline void -forEachItem( - ReadView const& view, - AccountID const& id, - std::function const&)> const& f) +forEachItem(ReadView const& view, AccountID const& id, std::function const& f) { forEachItem(view, keylet::ownerDir(id), f); } @@ -205,7 +199,7 @@ forEachItemAfter( uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)> const& f) + std::function const& f) { return forEachItemAfter(view, keylet::ownerDir(id), after, hint, limit, f); } diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h index 859981cf05..bdb83230eb 100644 --- a/include/xrpl/ledger/helpers/EscrowHelpers.h +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -18,7 +18,7 @@ TER escrowUnlockApplyHelper( ApplyView& view, Rate lockedRate, - std::shared_ptr const& sleDest, + SLE::ref sleDest, STAmount const& xrpBalance, STAmount const& amount, AccountID const& issuer, @@ -32,7 +32,7 @@ inline TER escrowUnlockApplyHelper( ApplyView& view, Rate lockedRate, - std::shared_ptr const& sleDest, + SLE::ref sleDest, STAmount const& xrpBalance, STAmount const& amount, AccountID const& issuer, @@ -162,7 +162,7 @@ inline TER escrowUnlockApplyHelper( ApplyView& view, Rate lockedRate, - std::shared_ptr const& sleDest, + SLE::ref sleDest, STAmount const& xrpBalance, STAmount const& amount, AccountID const& issuer, diff --git a/include/xrpl/ledger/helpers/NFTokenHelpers.h b/include/xrpl/ledger/helpers/NFTokenHelpers.h index 4294e1ca13..362cfe5a8c 100644 --- a/include/xrpl/ledger/helpers/NFTokenHelpers.h +++ b/include/xrpl/ledger/helpers/NFTokenHelpers.h @@ -28,10 +28,9 @@ findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID struct TokenAndPage { STObject token; - std::shared_ptr page; + SLE::pointer page; - TokenAndPage(STObject token, std::shared_ptr page) - : token(std::move(token)), page(std::move(page)) + TokenAndPage(STObject token, SLE::pointer page) : token(std::move(token)), page(std::move(page)) { } }; @@ -47,11 +46,7 @@ TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID); TER -removeToken( - ApplyView& view, - AccountID const& owner, - uint256 const& nftokenID, - std::shared_ptr const& page); +removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, SLE::ref page); /** Deletes the given token offer. @@ -63,7 +58,7 @@ removeToken( The offer also consumes one incremental reserve. */ bool -deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer); +deleteTokenOffer(ApplyView& view, SLE::ref offer); /** Repairs the links in an NFTokenPage directory. diff --git a/include/xrpl/ledger/helpers/OfferHelpers.h b/include/xrpl/ledger/helpers/OfferHelpers.h index 9096071811..fc863dff0a 100644 --- a/include/xrpl/ledger/helpers/OfferHelpers.h +++ b/include/xrpl/ledger/helpers/OfferHelpers.h @@ -5,8 +5,6 @@ #include #include -#include - namespace xrpl { /** Delete an offer. @@ -23,6 +21,6 @@ namespace xrpl { */ // [[nodiscard]] // nodiscard commented out so Flow, BookTip and others compile. TER -offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); +offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h index 24838f1331..810907b0af 100644 --- a/include/xrpl/ledger/helpers/PaymentChannelHelpers.h +++ b/include/xrpl/ledger/helpers/PaymentChannelHelpers.h @@ -8,10 +8,6 @@ namespace xrpl { TER -closeChannel( - std::shared_ptr const& slep, - ApplyView& view, - uint256 const& key, - beast::Journal j); +closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j); } // namespace xrpl diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h index 2616a6d5c9..3aaaa541fd 100644 --- a/include/xrpl/ledger/helpers/RippleStateHelpers.h +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -154,7 +154,7 @@ trustCreate( [[nodiscard]] TER trustDelete( ApplyView& view, - std::shared_ptr const& sleRippleState, + SLE::ref sleRippleState, AccountID const& uLowAccountID, AccountID const& uHighAccountID, beast::Journal j); @@ -248,7 +248,7 @@ removeEmptyHolding( [[nodiscard]] TER deleteAMMTrustLine( ApplyView& view, - std::shared_ptr sleState, + SLE::pointer sleState, std::optional const& ammAccountID, beast::Journal j); @@ -258,7 +258,7 @@ deleteAMMTrustLine( [[nodiscard]] TER deleteAMMMPToken( ApplyView& view, - std::shared_ptr sleMPT, + SLE::pointer sleMPT, AccountID const& ammAccountID, beast::Journal j); diff --git a/include/xrpl/ledger/helpers/VaultHelpers.h b/include/xrpl/ledger/helpers/VaultHelpers.h index 29270d913f..2344b4de77 100644 --- a/include/xrpl/ledger/helpers/VaultHelpers.h +++ b/include/xrpl/ledger/helpers/VaultHelpers.h @@ -5,7 +5,6 @@ #include #include -#include #include namespace xrpl { @@ -21,10 +20,7 @@ namespace xrpl { @return The number of shares, or nullopt on error. */ [[nodiscard]] std::optional -assetsToSharesDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets); +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 @@ -37,10 +33,7 @@ assetsToSharesDeposit( @return The number of assets, or nullopt on error. */ [[nodiscard]] std::optional -sharesToAssetsDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares); +sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares); /** Controls whether to truncate shares instead of rounding. */ enum class TruncateShares : bool { No = false, Yes = true }; @@ -69,8 +62,8 @@ enum class WaiveUnrealizedLoss : bool { No = false, Yes = true }; */ [[nodiscard]] std::optional assetsToSharesWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, + SLE::const_ref vault, + SLE::const_ref issuance, STAmount const& assets, TruncateShares truncate = TruncateShares::No, WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); @@ -89,8 +82,8 @@ assetsToSharesWithdraw( */ [[nodiscard]] std::optional sharesToAssetsWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, + SLE::const_ref vault, + SLE::const_ref issuance, STAmount const& shares, WaiveUnrealizedLoss waive = WaiveUnrealizedLoss::No); @@ -104,9 +97,6 @@ sharesToAssetsWithdraw( both the share MPTID and the outstanding-amount total. */ [[nodiscard]] bool -isSoleShareholder( - ReadView const& view, - AccountID const& account, - std::shared_ptr const& issuance); +isSoleShareholder(ReadView const& view, AccountID const& account, SLE::const_ref issuance); } // namespace xrpl diff --git a/include/xrpl/protocol_autogen/LedgerEntryBase.h b/include/xrpl/protocol_autogen/LedgerEntryBase.h index ad513992c7..5758adbb24 100644 --- a/include/xrpl/protocol_autogen/LedgerEntryBase.h +++ b/include/xrpl/protocol_autogen/LedgerEntryBase.h @@ -27,7 +27,7 @@ public: * @brief Construct a ledger entry wrapper from an existing SLE object. * @param sle The underlying serialized ledger entry to wrap */ - explicit LedgerEntryBase(std::shared_ptr sle) : sle_(std::move(sle)) + explicit LedgerEntryBase(SLE::const_pointer sle) : sle_(std::move(sle)) { } @@ -151,7 +151,7 @@ public: * @return A constant reference to the underlying SLE object */ [[nodiscard]] - std::shared_ptr + SLE::const_pointer getSle() const { return sle_; @@ -159,7 +159,7 @@ public: protected: /** @brief The underlying serialized ledger entry being wrapped. */ - std::shared_ptr sle_; + SLE::const_pointer sle_; }; } // namespace xrpl::ledger_entries diff --git a/include/xrpl/protocol_autogen/ledger_entries/AMM.h b/include/xrpl/protocol_autogen/ledger_entries/AMM.h index 529e3f4df7..11fd3738c9 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/AMM.h +++ b/include/xrpl/protocol_autogen/ledger_entries/AMM.h @@ -33,7 +33,7 @@ public: * @brief Construct a AMM ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit AMM(std::shared_ptr sle) + explicit AMM(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -256,7 +256,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - AMMBuilder(std::shared_ptr sle) + AMMBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltAMM) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h index e20259330d..f9a12a027f 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h +++ b/include/xrpl/protocol_autogen/ledger_entries/AccountRoot.h @@ -33,7 +33,7 @@ public: * @brief Construct a AccountRoot ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit AccountRoot(std::shared_ptr sle) + explicit AccountRoot(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -555,7 +555,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - AccountRootBuilder(std::shared_ptr sle) + AccountRootBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltACCOUNT_ROOT) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Amendments.h b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h index 4f1b316b99..6a801308ca 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Amendments.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Amendments.h @@ -33,7 +33,7 @@ public: * @brief Construct a Amendments ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Amendments(std::shared_ptr sle) + explicit Amendments(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -166,7 +166,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - AmendmentsBuilder(std::shared_ptr sle) + AmendmentsBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltAMENDMENTS) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Bridge.h b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h index fd7df1977f..2c7479b243 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Bridge.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Bridge.h @@ -33,7 +33,7 @@ public: * @brief Construct a Bridge ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Bridge(std::shared_ptr sle) + explicit Bridge(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -210,7 +210,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - BridgeBuilder(std::shared_ptr sle) + BridgeBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltBRIDGE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Check.h b/include/xrpl/protocol_autogen/ledger_entries/Check.h index 750270cad9..5b3fd10b92 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Check.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Check.h @@ -33,7 +33,7 @@ public: * @brief Construct a Check ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Check(std::shared_ptr sle) + explicit Check(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -269,7 +269,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - CheckBuilder(std::shared_ptr sle) + CheckBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltCHECK) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Credential.h b/include/xrpl/protocol_autogen/ledger_entries/Credential.h index 5433f00b33..dfce76e45c 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Credential.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Credential.h @@ -33,7 +33,7 @@ public: * @brief Construct a Credential ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Credential(std::shared_ptr sle) + explicit Credential(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -219,7 +219,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - CredentialBuilder(std::shared_ptr sle) + CredentialBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltCREDENTIAL) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/DID.h b/include/xrpl/protocol_autogen/ledger_entries/DID.h index 2f57961be4..ad423377e7 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/DID.h +++ b/include/xrpl/protocol_autogen/ledger_entries/DID.h @@ -33,7 +33,7 @@ public: * @brief Construct a DID ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit DID(std::shared_ptr sle) + explicit DID(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -193,7 +193,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - DIDBuilder(std::shared_ptr sle) + DIDBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltDID) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Delegate.h b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h index 7458b8103a..bfe5f5587a 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Delegate.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Delegate.h @@ -33,7 +33,7 @@ public: * @brief Construct a Delegate ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Delegate(std::shared_ptr sle) + explicit Delegate(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -172,7 +172,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - DelegateBuilder(std::shared_ptr sle) + DelegateBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltDELEGATE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h index 42b430a665..069bed6b77 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h +++ b/include/xrpl/protocol_autogen/ledger_entries/DepositPreauth.h @@ -33,7 +33,7 @@ public: * @brief Construct a DepositPreauth ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit DepositPreauth(std::shared_ptr sle) + explicit DepositPreauth(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -170,7 +170,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - DepositPreauthBuilder(std::shared_ptr sle) + DepositPreauthBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltDEPOSIT_PREAUTH) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h index e47cb98a26..50659c33f6 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h +++ b/include/xrpl/protocol_autogen/ledger_entries/DirectoryNode.h @@ -33,7 +33,7 @@ public: * @brief Construct a DirectoryNode ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit DirectoryNode(std::shared_ptr sle) + explicit DirectoryNode(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -431,7 +431,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - DirectoryNodeBuilder(std::shared_ptr sle) + DirectoryNodeBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltDIR_NODE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Escrow.h b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h index ae8219a4f0..f3c033d26d 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Escrow.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Escrow.h @@ -33,7 +33,7 @@ public: * @brief Construct a Escrow ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Escrow(std::shared_ptr sle) + explicit Escrow(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -363,7 +363,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - EscrowBuilder(std::shared_ptr sle) + EscrowBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltESCROW) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h index cfd7a591e8..8f43d3b782 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h +++ b/include/xrpl/protocol_autogen/ledger_entries/FeeSettings.h @@ -33,7 +33,7 @@ public: * @brief Construct a FeeSettings ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit FeeSettings(std::shared_ptr sle) + explicit FeeSettings(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -285,7 +285,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - FeeSettingsBuilder(std::shared_ptr sle) + FeeSettingsBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltFEE_SETTINGS) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h index 8c082be685..f1d3684b55 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h +++ b/include/xrpl/protocol_autogen/ledger_entries/LedgerHashes.h @@ -33,7 +33,7 @@ public: * @brief Construct a LedgerHashes ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit LedgerHashes(std::shared_ptr sle) + explicit LedgerHashes(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -130,7 +130,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - LedgerHashesBuilder(std::shared_ptr sle) + LedgerHashesBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltLEDGER_HASHES) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Loan.h b/include/xrpl/protocol_autogen/ledger_entries/Loan.h index 4408c278db..5d837736ec 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Loan.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Loan.h @@ -33,7 +33,7 @@ public: * @brief Construct a Loan ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Loan(std::shared_ptr sle) + explicit Loan(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -607,7 +607,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - LoanBuilder(std::shared_ptr sle) + LoanBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltLOAN) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h index b1c6dd8a54..88f05e3433 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h +++ b/include/xrpl/protocol_autogen/ledger_entries/LoanBroker.h @@ -33,7 +33,7 @@ public: * @brief Construct a LoanBroker ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit LoanBroker(std::shared_ptr sle) + explicit LoanBroker(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -378,7 +378,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - LoanBrokerBuilder(std::shared_ptr sle) + LoanBrokerBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltLOAN_BROKER) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPToken.h b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h index 8176b74fe9..0d394020f7 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/MPToken.h +++ b/include/xrpl/protocol_autogen/ledger_entries/MPToken.h @@ -33,7 +33,7 @@ public: * @brief Construct a MPToken ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit MPToken(std::shared_ptr sle) + explicit MPToken(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -182,7 +182,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - MPTokenBuilder(std::shared_ptr sle) + MPTokenBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltMPTOKEN) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h index 7f772b1c74..d493ac779c 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h +++ b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h @@ -33,7 +33,7 @@ public: * @brief Construct a MPTokenIssuance ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit MPTokenIssuance(std::shared_ptr sle) + explicit MPTokenIssuance(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -339,7 +339,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - MPTokenIssuanceBuilder(std::shared_ptr sle) + MPTokenIssuanceBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltMPTOKEN_ISSUANCE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h index c0a6ee6cbc..072d3721f9 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenOffer.h @@ -33,7 +33,7 @@ public: * @brief Construct a NFTokenOffer ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit NFTokenOffer(std::shared_ptr sle) + explicit NFTokenOffer(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -208,7 +208,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - NFTokenOfferBuilder(std::shared_ptr sle) + NFTokenOfferBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltNFTOKEN_OFFER) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h index c4190f9068..5e00cb1120 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h +++ b/include/xrpl/protocol_autogen/ledger_entries/NFTokenPage.h @@ -33,7 +33,7 @@ public: * @brief Construct a NFTokenPage ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit NFTokenPage(std::shared_ptr sle) + explicit NFTokenPage(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -157,7 +157,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - NFTokenPageBuilder(std::shared_ptr sle) + NFTokenPageBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltNFTOKEN_PAGE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h index b09135e5f8..7ca9729082 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h +++ b/include/xrpl/protocol_autogen/ledger_entries/NegativeUNL.h @@ -33,7 +33,7 @@ public: * @brief Construct a NegativeUNL ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit NegativeUNL(std::shared_ptr sle) + explicit NegativeUNL(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -190,7 +190,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - NegativeUNLBuilder(std::shared_ptr sle) + NegativeUNLBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltNEGATIVE_UNL) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Offer.h b/include/xrpl/protocol_autogen/ledger_entries/Offer.h index a6d427cbbd..f51b54cfd2 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Offer.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Offer.h @@ -33,7 +33,7 @@ public: * @brief Construct a Offer ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Offer(std::shared_ptr sle) + explicit Offer(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -259,7 +259,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - OfferBuilder(std::shared_ptr sle) + OfferBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltOFFER) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Oracle.h b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h index 31735a713c..902032f94f 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Oracle.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Oracle.h @@ -33,7 +33,7 @@ public: * @brief Construct a Oracle ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Oracle(std::shared_ptr sle) + explicit Oracle(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -222,7 +222,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - OracleBuilder(std::shared_ptr sle) + OracleBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltORACLE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h index 097f287425..61a4e2d044 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h +++ b/include/xrpl/protocol_autogen/ledger_entries/PayChannel.h @@ -33,7 +33,7 @@ public: * @brief Construct a PayChannel ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit PayChannel(std::shared_ptr sle) + explicit PayChannel(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -330,7 +330,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - PayChannelBuilder(std::shared_ptr sle) + PayChannelBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltPAYCHAN) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h index cc5a1649ef..638dda2420 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h +++ b/include/xrpl/protocol_autogen/ledger_entries/PermissionedDomain.h @@ -33,7 +33,7 @@ public: * @brief Construct a PermissionedDomain ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit PermissionedDomain(std::shared_ptr sle) + explicit PermissionedDomain(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -148,7 +148,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - PermissionedDomainBuilder(std::shared_ptr sle) + PermissionedDomainBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltPERMISSIONED_DOMAIN) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h index cb221bb2ad..e8debfe792 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/RippleState.h +++ b/include/xrpl/protocol_autogen/ledger_entries/RippleState.h @@ -33,7 +33,7 @@ public: * @brief Construct a RippleState ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit RippleState(std::shared_ptr sle) + explicit RippleState(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -278,7 +278,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - RippleStateBuilder(std::shared_ptr sle) + RippleStateBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltRIPPLE_STATE) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/SignerList.h b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h index dfc7eb4506..443e5588f9 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/SignerList.h +++ b/include/xrpl/protocol_autogen/ledger_entries/SignerList.h @@ -33,7 +33,7 @@ public: * @brief Construct a SignerList ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit SignerList(std::shared_ptr sle) + explicit SignerList(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -172,7 +172,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - SignerListBuilder(std::shared_ptr sle) + SignerListBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltSIGNER_LIST) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Ticket.h b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h index ceadd70765..6fa5b57f6c 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Ticket.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Ticket.h @@ -33,7 +33,7 @@ public: * @brief Construct a Ticket ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Ticket(std::shared_ptr sle) + explicit Ticket(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -134,7 +134,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - TicketBuilder(std::shared_ptr sle) + TicketBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltTICKET) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/Vault.h b/include/xrpl/protocol_autogen/ledger_entries/Vault.h index 168894d8d2..d1aaeb4ed8 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/Vault.h +++ b/include/xrpl/protocol_autogen/ledger_entries/Vault.h @@ -33,7 +33,7 @@ public: * @brief Construct a Vault ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit Vault(std::shared_ptr sle) + explicit Vault(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -330,7 +330,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - VaultBuilder(std::shared_ptr sle) + VaultBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltVAULT) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h index 4a3e9c9103..3f8058a4a1 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedClaimID.h @@ -33,7 +33,7 @@ public: * @brief Construct a XChainOwnedClaimID ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit XChainOwnedClaimID(std::shared_ptr sle) + explicit XChainOwnedClaimID(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -187,7 +187,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - XChainOwnedClaimIDBuilder(std::shared_ptr sle) + XChainOwnedClaimIDBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CLAIM_ID) { diff --git a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h index 542de104ea..e24009a4b7 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h +++ b/include/xrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimID.h @@ -33,7 +33,7 @@ public: * @brief Construct a XChainOwnedCreateAccountClaimID ledger entry wrapper from an existing SLE object. * @throws std::runtime_error if the ledger entry type doesn't match. */ - explicit XChainOwnedCreateAccountClaimID(std::shared_ptr sle) + explicit XChainOwnedCreateAccountClaimID(SLE::const_pointer sle) : LedgerEntryBase(std::move(sle)) { // Verify ledger entry type @@ -161,7 +161,7 @@ public: * @param sle The existing ledger entry to copy from. * @throws std::runtime_error if the ledger entry type doesn't match. */ - XChainOwnedCreateAccountClaimIDBuilder(std::shared_ptr sle) + XChainOwnedCreateAccountClaimIDBuilder(SLE::const_pointer sle) { if (sle->at(sfLedgerEntryType) != ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID) { diff --git a/include/xrpl/tx/ApplyContext.h b/include/xrpl/tx/ApplyContext.h index 910ec6be42..8540037601 100644 --- a/include/xrpl/tx/ApplyContext.h +++ b/include/xrpl/tx/ApplyContext.h @@ -93,8 +93,8 @@ public: std::function const& before, - std::shared_ptr const& after)> const& func); + SLE::const_ref before, + SLE::const_ref after)> const& func); void destroyXRP(XRPAmount const& fee) diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index 1440a5097f..86b1e856b3 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -263,10 +263,7 @@ protected: * to detect deletions. */ virtual void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) = 0; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) = 0; /** Check transaction-specific post-conditions after all entries have * been visited. @@ -368,7 +365,7 @@ private: ReadView const& view, AccountID const& idSigner, AccountID const& idAccount, - std::shared_ptr sleAccount, + SLE::const_pointer sleAccount, beast::Journal const j); static NotTEC checkMultiSign( diff --git a/include/xrpl/tx/invariants/AMMInvariant.h b/include/xrpl/tx/invariants/AMMInvariant.h index 43d9c5ad0a..ee2fb66a1c 100644 --- a/include/xrpl/tx/invariants/AMMInvariant.h +++ b/include/xrpl/tx/invariants/AMMInvariant.h @@ -22,7 +22,7 @@ public: ValidAMM() = default; void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/FreezeInvariant.h b/include/xrpl/tx/invariants/FreezeInvariant.h index 645f444462..a76eb66497 100644 --- a/include/xrpl/tx/invariants/FreezeInvariant.h +++ b/include/xrpl/tx/invariants/FreezeInvariant.h @@ -22,7 +22,7 @@ class TransfersNotFrozen { struct BalanceChange { - std::shared_ptr const line; + SLE::const_pointer const line; int const balanceChangeSign; }; @@ -35,37 +35,34 @@ class TransfersNotFrozen using ByIssuer = std::map; ByIssuer balanceChanges_; - std::map const> possibleIssuers_; + std::map possibleIssuers_; public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); private: bool - isValidEntry(std::shared_ptr const& before, std::shared_ptr const& after); + isValidEntry(SLE::const_ref before, SLE::const_ref after); static STAmount - calculateBalanceChange( - std::shared_ptr const& before, - std::shared_ptr const& after, - bool isDelete); + calculateBalanceChange(SLE::const_ref before, SLE::const_ref after, bool isDelete); void recordBalance(Issue const& issue, BalanceChange change); void - recordBalanceChanges(std::shared_ptr const& after, STAmount const& balanceChange); + recordBalanceChanges(SLE::const_ref after, STAmount const& balanceChange); - std::shared_ptr + SLE::const_pointer findIssuer(AccountID const& issuerID, ReadView const& view); static bool validateIssuerChanges( - std::shared_ptr const& issuer, + SLE::const_ref issuer, IssuerChanges const& changes, STTx const& tx, beast::Journal const& j, diff --git a/include/xrpl/tx/invariants/InvariantCheck.h b/include/xrpl/tx/invariants/InvariantCheck.h index d4c0154269..9378062726 100644 --- a/include/xrpl/tx/invariants/InvariantCheck.h +++ b/include/xrpl/tx/invariants/InvariantCheck.h @@ -70,10 +70,7 @@ public: * @param after ledger entry after modification by the transaction */ void - visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after); + visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after); /** * @brief called after all ledger entries have been visited to determine @@ -111,7 +108,7 @@ class TransactionFeeCheck { public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); static bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); @@ -131,7 +128,7 @@ class XRPNotCreated public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -151,7 +148,7 @@ class AccountRootsNotDeleted public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -174,11 +171,11 @@ class AccountRootsDeletedClean // deleted, it can still be found. After is used specifically for any checks // that are expected as part of the deletion, such as zeroing out the // balance. - std::vector, std::shared_ptr>> accountsDeleted_; + std::vector> accountsDeleted_; public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); @@ -197,7 +194,7 @@ class XRPBalanceChecks public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -214,7 +211,7 @@ class LedgerEntryTypesMatch public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -232,7 +229,7 @@ class NoXRPTrustLines public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -251,7 +248,7 @@ class NoDeepFreezeTrustLinesWithoutFreeze public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -270,7 +267,7 @@ class NoBadOffers public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -286,7 +283,7 @@ class NoZeroEscrow public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -306,7 +303,7 @@ class ValidNewAccountRoot public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -327,7 +324,7 @@ class ValidClawback public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -347,7 +344,7 @@ class ValidPseudoAccounts public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); @@ -367,7 +364,7 @@ class NoModifiedUnmodifiableFields public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/LoanBrokerInvariant.h b/include/xrpl/tx/invariants/LoanBrokerInvariant.h index e7d14a638b..684bbff423 100644 --- a/include/xrpl/tx/invariants/LoanBrokerInvariant.h +++ b/include/xrpl/tx/invariants/LoanBrokerInvariant.h @@ -46,7 +46,7 @@ class ValidLoanBroker public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/LoanInvariant.h b/include/xrpl/tx/invariants/LoanInvariant.h index bda9c51653..3f408d169a 100644 --- a/include/xrpl/tx/invariants/LoanInvariant.h +++ b/include/xrpl/tx/invariants/LoanInvariant.h @@ -23,7 +23,7 @@ class ValidLoan public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/MPTInvariant.h b/include/xrpl/tx/invariants/MPTInvariant.h index becb752126..b4b76a290f 100644 --- a/include/xrpl/tx/invariants/MPTInvariant.h +++ b/include/xrpl/tx/invariants/MPTInvariant.h @@ -37,7 +37,7 @@ class ValidMPTIssuance public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -65,7 +65,7 @@ class ValidMPTPayment public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/NFTInvariant.h b/include/xrpl/tx/invariants/NFTInvariant.h index fa056ecbb0..698df05247 100644 --- a/include/xrpl/tx/invariants/NFTInvariant.h +++ b/include/xrpl/tx/invariants/NFTInvariant.h @@ -33,7 +33,7 @@ class ValidNFTokenPage public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; @@ -61,7 +61,7 @@ class NFTokenCountTracking public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); [[nodiscard]] bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const; diff --git a/include/xrpl/tx/invariants/PermissionedDEXInvariant.h b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h index 654ed3e009..2ec22ded88 100644 --- a/include/xrpl/tx/invariants/PermissionedDEXInvariant.h +++ b/include/xrpl/tx/invariants/PermissionedDEXInvariant.h @@ -18,7 +18,7 @@ class ValidPermissionedDEX public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/PermissionedDomainInvariant.h b/include/xrpl/tx/invariants/PermissionedDomainInvariant.h index 2475ed8f6b..19edcc0b39 100644 --- a/include/xrpl/tx/invariants/PermissionedDomainInvariant.h +++ b/include/xrpl/tx/invariants/PermissionedDomainInvariant.h @@ -32,7 +32,7 @@ class ValidPermissionedDomain public: void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/invariants/VaultInvariant.h b/include/xrpl/tx/invariants/VaultInvariant.h index abc256c880..2a9ffc8282 100644 --- a/include/xrpl/tx/invariants/VaultInvariant.h +++ b/include/xrpl/tx/invariants/VaultInvariant.h @@ -154,7 +154,7 @@ public: computeCoarsestScale(std::vector const& numbers); void - visitEntry(bool, std::shared_ptr const&, std::shared_ptr const&); + visitEntry(bool, SLE::const_ref, SLE::const_ref); bool finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&); diff --git a/include/xrpl/tx/paths/BookTip.h b/include/xrpl/tx/paths/BookTip.h index e06a2da86c..c4bdb0415c 100644 --- a/include/xrpl/tx/paths/BookTip.h +++ b/include/xrpl/tx/paths/BookTip.h @@ -21,7 +21,7 @@ private: uint256 end_; uint256 dir_; uint256 index_; - std::shared_ptr entry_; + SLE::pointer entry_; Quality quality_{}; public: diff --git a/include/xrpl/tx/transactors/account/AccountDelete.h b/include/xrpl/tx/transactors/account/AccountDelete.h index d2cbfa5ad2..16661a4b7c 100644 --- a/include/xrpl/tx/transactors/account/AccountDelete.h +++ b/include/xrpl/tx/transactors/account/AccountDelete.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/account/AccountSet.h b/include/xrpl/tx/transactors/account/AccountSet.h index 002779db64..a40a9ec963 100644 --- a/include/xrpl/tx/transactors/account/AccountSet.h +++ b/include/xrpl/tx/transactors/account/AccountSet.h @@ -33,10 +33,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/account/SetRegularKey.h b/include/xrpl/tx/transactors/account/SetRegularKey.h index a9f1ce715d..6ff9c5aa52 100644 --- a/include/xrpl/tx/transactors/account/SetRegularKey.h +++ b/include/xrpl/tx/transactors/account/SetRegularKey.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/account/SignerListSet.h b/include/xrpl/tx/transactors/account/SignerListSet.h index 760f6e9358..46e3191323 100644 --- a/include/xrpl/tx/transactors/account/SignerListSet.h +++ b/include/xrpl/tx/transactors/account/SignerListSet.h @@ -42,10 +42,7 @@ public: preCompute() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/bridge/XChainBridge.h b/include/xrpl/tx/transactors/bridge/XChainBridge.h index 1033dee188..58a546de2f 100644 --- a/include/xrpl/tx/transactors/bridge/XChainBridge.h +++ b/include/xrpl/tx/transactors/bridge/XChainBridge.h @@ -28,10 +28,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -64,10 +61,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -111,10 +105,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -152,10 +143,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -195,10 +183,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -238,10 +223,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -272,10 +254,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -330,10 +309,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/check/CheckCancel.h b/include/xrpl/tx/transactors/check/CheckCancel.h index 5fdaa7e527..b8e8b6c52d 100644 --- a/include/xrpl/tx/transactors/check/CheckCancel.h +++ b/include/xrpl/tx/transactors/check/CheckCancel.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/check/CheckCash.h b/include/xrpl/tx/transactors/check/CheckCash.h index ad9de1e4c3..7d4e615cfd 100644 --- a/include/xrpl/tx/transactors/check/CheckCash.h +++ b/include/xrpl/tx/transactors/check/CheckCash.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/check/CheckCreate.h b/include/xrpl/tx/transactors/check/CheckCreate.h index e03677e5f5..178fe4707c 100644 --- a/include/xrpl/tx/transactors/check/CheckCreate.h +++ b/include/xrpl/tx/transactors/check/CheckCreate.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/credentials/CredentialAccept.h b/include/xrpl/tx/transactors/credentials/CredentialAccept.h index 97838f8a0a..8630ac3f7f 100644 --- a/include/xrpl/tx/transactors/credentials/CredentialAccept.h +++ b/include/xrpl/tx/transactors/credentials/CredentialAccept.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/credentials/CredentialCreate.h b/include/xrpl/tx/transactors/credentials/CredentialCreate.h index 7493aa4dc5..91b5e829d3 100644 --- a/include/xrpl/tx/transactors/credentials/CredentialCreate.h +++ b/include/xrpl/tx/transactors/credentials/CredentialCreate.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/credentials/CredentialDelete.h b/include/xrpl/tx/transactors/credentials/CredentialDelete.h index 4d9b4ddd18..70fe5cb3d0 100644 --- a/include/xrpl/tx/transactors/credentials/CredentialDelete.h +++ b/include/xrpl/tx/transactors/credentials/CredentialDelete.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/delegate/DelegateSet.h b/include/xrpl/tx/transactors/delegate/DelegateSet.h index c93c48e970..a55524ecff 100644 --- a/include/xrpl/tx/transactors/delegate/DelegateSet.h +++ b/include/xrpl/tx/transactors/delegate/DelegateSet.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -38,7 +35,7 @@ public: // Interface used by AccountDelete static TER - deleteDelegate(ApplyView& view, std::shared_ptr const& sle, beast::Journal j); + deleteDelegate(ApplyView& view, SLE::ref sle, beast::Journal j); }; } // namespace xrpl diff --git a/include/xrpl/tx/transactors/dex/AMMBid.h b/include/xrpl/tx/transactors/dex/AMMBid.h index 9328c48e79..dfa50d06ba 100644 --- a/include/xrpl/tx/transactors/dex/AMMBid.h +++ b/include/xrpl/tx/transactors/dex/AMMBid.h @@ -64,10 +64,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMClawback.h b/include/xrpl/tx/transactors/dex/AMMClawback.h index 7ac03ebb28..6f31480490 100644 --- a/include/xrpl/tx/transactors/dex/AMMClawback.h +++ b/include/xrpl/tx/transactors/dex/AMMClawback.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMCreate.h b/include/xrpl/tx/transactors/dex/AMMCreate.h index 04d6fe6f60..64d2a1e3b1 100644 --- a/include/xrpl/tx/transactors/dex/AMMCreate.h +++ b/include/xrpl/tx/transactors/dex/AMMCreate.h @@ -60,10 +60,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMDelete.h b/include/xrpl/tx/transactors/dex/AMMDelete.h index ff5776e3b7..d3e8cfeeb4 100644 --- a/include/xrpl/tx/transactors/dex/AMMDelete.h +++ b/include/xrpl/tx/transactors/dex/AMMDelete.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMDeposit.h b/include/xrpl/tx/transactors/dex/AMMDeposit.h index 9be53167f2..453046ddad 100644 --- a/include/xrpl/tx/transactors/dex/AMMDeposit.h +++ b/include/xrpl/tx/transactors/dex/AMMDeposit.h @@ -64,10 +64,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMVote.h b/include/xrpl/tx/transactors/dex/AMMVote.h index 1b5946aae1..8defc1369e 100644 --- a/include/xrpl/tx/transactors/dex/AMMVote.h +++ b/include/xrpl/tx/transactors/dex/AMMVote.h @@ -49,10 +49,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/AMMWithdraw.h b/include/xrpl/tx/transactors/dex/AMMWithdraw.h index 9e6eb62d51..6e88320eae 100644 --- a/include/xrpl/tx/transactors/dex/AMMWithdraw.h +++ b/include/xrpl/tx/transactors/dex/AMMWithdraw.h @@ -72,10 +72,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -155,7 +152,7 @@ public: static std::pair deleteAMMAccountIfEmpty( Sandbox& sb, - std::shared_ptr const ammSle, + SLE::pointer const ammSle, STAmount const& lpTokenBalance, Asset const& asset1, Asset const& asset2, diff --git a/include/xrpl/tx/transactors/dex/OfferCancel.h b/include/xrpl/tx/transactors/dex/OfferCancel.h index b2641049e6..2806b6942f 100644 --- a/include/xrpl/tx/transactors/dex/OfferCancel.h +++ b/include/xrpl/tx/transactors/dex/OfferCancel.h @@ -24,10 +24,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/dex/OfferCreate.h b/include/xrpl/tx/transactors/dex/OfferCreate.h index efae5312e2..7faf613d3a 100644 --- a/include/xrpl/tx/transactors/dex/OfferCreate.h +++ b/include/xrpl/tx/transactors/dex/OfferCreate.h @@ -41,10 +41,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -81,7 +78,7 @@ private: TER applyHybrid( Sandbox& sb, - std::shared_ptr sleOffer, + STLedgerEntry::pointer sleOffer, Keylet const& offerIndex, STAmount const& saTakerPays, STAmount const& saTakerGets, diff --git a/include/xrpl/tx/transactors/did/DIDDelete.h b/include/xrpl/tx/transactors/did/DIDDelete.h index c750d4c95e..94615f51b6 100644 --- a/include/xrpl/tx/transactors/did/DIDDelete.h +++ b/include/xrpl/tx/transactors/did/DIDDelete.h @@ -20,16 +20,13 @@ public: deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner); static TER - deleteSLE(ApplyView& view, std::shared_ptr sle, AccountID const owner, beast::Journal j); + deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, beast::Journal j); TER doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/did/DIDSet.h b/include/xrpl/tx/transactors/did/DIDSet.h index b2c3d97c81..8c84ea6c9b 100644 --- a/include/xrpl/tx/transactors/did/DIDSet.h +++ b/include/xrpl/tx/transactors/did/DIDSet.h @@ -20,10 +20,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/escrow/EscrowCancel.h b/include/xrpl/tx/transactors/escrow/EscrowCancel.h index 92b55374d5..af09f70202 100644 --- a/include/xrpl/tx/transactors/escrow/EscrowCancel.h +++ b/include/xrpl/tx/transactors/escrow/EscrowCancel.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/escrow/EscrowCreate.h b/include/xrpl/tx/transactors/escrow/EscrowCreate.h index 2e9da89896..8800e97b80 100644 --- a/include/xrpl/tx/transactors/escrow/EscrowCreate.h +++ b/include/xrpl/tx/transactors/escrow/EscrowCreate.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/escrow/EscrowFinish.h b/include/xrpl/tx/transactors/escrow/EscrowFinish.h index 806f947b8b..061fa0527c 100644 --- a/include/xrpl/tx/transactors/escrow/EscrowFinish.h +++ b/include/xrpl/tx/transactors/escrow/EscrowFinish.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h index 1b86ac41e7..81ea97ce7e 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerCoverClawback.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h index 63e96457dc..43b932726b 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerCoverDeposit.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h b/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h index e3182c0851..a757ac51bf 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h b/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h index 464a43e398..0ce5f29387 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerDelete.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanBrokerSet.h b/include/xrpl/tx/transactors/lending/LoanBrokerSet.h index 72a339951b..75175e2dd6 100644 --- a/include/xrpl/tx/transactors/lending/LoanBrokerSet.h +++ b/include/xrpl/tx/transactors/lending/LoanBrokerSet.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanDelete.h b/include/xrpl/tx/transactors/lending/LoanDelete.h index 8dd9c4601b..9e8c3c172a 100644 --- a/include/xrpl/tx/transactors/lending/LoanDelete.h +++ b/include/xrpl/tx/transactors/lending/LoanDelete.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanManage.h b/include/xrpl/tx/transactors/lending/LoanManage.h index 08c873a5e7..d2344c0ec0 100644 --- a/include/xrpl/tx/transactors/lending/LoanManage.h +++ b/include/xrpl/tx/transactors/lending/LoanManage.h @@ -60,10 +60,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanPay.h b/include/xrpl/tx/transactors/lending/LoanPay.h index 561550e889..9be9695c64 100644 --- a/include/xrpl/tx/transactors/lending/LoanPay.h +++ b/include/xrpl/tx/transactors/lending/LoanPay.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/lending/LoanSet.h b/include/xrpl/tx/transactors/lending/LoanSet.h index 304c077f3d..d277629e44 100644 --- a/include/xrpl/tx/transactors/lending/LoanSet.h +++ b/include/xrpl/tx/transactors/lending/LoanSet.h @@ -39,10 +39,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h b/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h index 3c98d55141..c5cd10fa6a 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h +++ b/include/xrpl/tx/transactors/nft/NFTokenAcceptOffer.h @@ -11,10 +11,10 @@ private: pay(AccountID const& from, AccountID const& to, STAmount const& amount); TER - acceptOffer(std::shared_ptr const& offer); + acceptOffer(SLE::ref offer); TER - bridgeOffers(std::shared_ptr const& buy, std::shared_ptr const& sell); + bridgeOffers(SLE::ref buy, SLE::ref sell); TER transferNFToken(AccountID const& buyer, AccountID const& seller, uint256 const& nfTokenID); @@ -36,10 +36,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenBurn.h b/include/xrpl/tx/transactors/nft/NFTokenBurn.h index 4665a17aa7..849d09cb7e 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenBurn.h +++ b/include/xrpl/tx/transactors/nft/NFTokenBurn.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h b/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h index 3eae44b389..a74a1c3e59 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h +++ b/include/xrpl/tx/transactors/nft/NFTokenCancelOffer.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h b/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h index 20ccefc2cb..c874381dd0 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h +++ b/include/xrpl/tx/transactors/nft/NFTokenCreateOffer.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenMint.h b/include/xrpl/tx/transactors/nft/NFTokenMint.h index 1a0deae29d..9267e8e801 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenMint.h +++ b/include/xrpl/tx/transactors/nft/NFTokenMint.h @@ -31,10 +31,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/nft/NFTokenModify.h b/include/xrpl/tx/transactors/nft/NFTokenModify.h index 5b197d72b4..0d18e4a6d4 100644 --- a/include/xrpl/tx/transactors/nft/NFTokenModify.h +++ b/include/xrpl/tx/transactors/nft/NFTokenModify.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/oracle/OracleDelete.h b/include/xrpl/tx/transactors/oracle/OracleDelete.h index e83a334b87..c16d5fb2a9 100644 --- a/include/xrpl/tx/transactors/oracle/OracleDelete.h +++ b/include/xrpl/tx/transactors/oracle/OracleDelete.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -46,11 +43,7 @@ public: beast::Journal const& j) override; static TER - deleteOracle( - ApplyView& view, - std::shared_ptr const& sle, - AccountID const& account, - beast::Journal j); + deleteOracle(ApplyView& view, SLE::ref sle, AccountID const& account, beast::Journal j); }; } // namespace xrpl diff --git a/include/xrpl/tx/transactors/oracle/OracleSet.h b/include/xrpl/tx/transactors/oracle/OracleSet.h index 12c022470b..831c11b8c4 100644 --- a/include/xrpl/tx/transactors/oracle/OracleSet.h +++ b/include/xrpl/tx/transactors/oracle/OracleSet.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment/DepositPreauth.h b/include/xrpl/tx/transactors/payment/DepositPreauth.h index fbf22f8ca8..742b1ef3f7 100644 --- a/include/xrpl/tx/transactors/payment/DepositPreauth.h +++ b/include/xrpl/tx/transactors/payment/DepositPreauth.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment/Payment.h b/include/xrpl/tx/transactors/payment/Payment.h index ef42b67fa4..14897b4efe 100644 --- a/include/xrpl/tx/transactors/payment/Payment.h +++ b/include/xrpl/tx/transactors/payment/Payment.h @@ -41,10 +41,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h index 98d4638e51..e13fea6d6c 100644 --- a/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelClaim.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h index 73059a7e46..56e984cd57 100644 --- a/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h b/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h index 587ee9f778..272076ff20 100644 --- a/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h +++ b/include/xrpl/tx/transactors/payment_channel/PaymentChannelFund.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h index 77834f7683..88883fb86f 100644 --- a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h +++ b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h @@ -24,10 +24,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h index 47c35800dd..4afa8cef5a 100644 --- a/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h +++ b/include/xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h @@ -27,10 +27,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/system/Batch.h b/include/xrpl/tx/transactors/system/Batch.h index e190714725..43f0103319 100644 --- a/include/xrpl/tx/transactors/system/Batch.h +++ b/include/xrpl/tx/transactors/system/Batch.h @@ -34,10 +34,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/system/Change.h b/include/xrpl/tx/transactors/system/Change.h index 33df426593..339723ae8e 100644 --- a/include/xrpl/tx/transactors/system/Change.h +++ b/include/xrpl/tx/transactors/system/Change.h @@ -19,10 +19,7 @@ public: preCompute() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/system/LedgerStateFix.h b/include/xrpl/tx/transactors/system/LedgerStateFix.h index 6fbae6fc6a..973f89faa9 100644 --- a/include/xrpl/tx/transactors/system/LedgerStateFix.h +++ b/include/xrpl/tx/transactors/system/LedgerStateFix.h @@ -31,10 +31,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/system/TicketCreate.h b/include/xrpl/tx/transactors/system/TicketCreate.h index 4991d1d08b..5783faa6d1 100644 --- a/include/xrpl/tx/transactors/system/TicketCreate.h +++ b/include/xrpl/tx/transactors/system/TicketCreate.h @@ -61,10 +61,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/Clawback.h b/include/xrpl/tx/transactors/token/Clawback.h index 7c99cef0d2..ed90776e59 100644 --- a/include/xrpl/tx/transactors/token/Clawback.h +++ b/include/xrpl/tx/transactors/token/Clawback.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/MPTokenAuthorize.h b/include/xrpl/tx/transactors/token/MPTokenAuthorize.h index b2cfa74f6e..e30d123e52 100644 --- a/include/xrpl/tx/transactors/token/MPTokenAuthorize.h +++ b/include/xrpl/tx/transactors/token/MPTokenAuthorize.h @@ -35,10 +35,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h index 6c59d85548..a706c71e18 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h @@ -51,10 +51,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h index 65682c8f5e..21032e7337 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceDestroy.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h index 7397183bbf..6a6d1fc445 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceSet.h @@ -32,10 +32,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/token/TrustSet.h b/include/xrpl/tx/transactors/token/TrustSet.h index d439b676b5..dcf454bea1 100644 --- a/include/xrpl/tx/transactors/token/TrustSet.h +++ b/include/xrpl/tx/transactors/token/TrustSet.h @@ -30,10 +30,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultClawback.h b/include/xrpl/tx/transactors/vault/VaultClawback.h index 4b9e283571..b8032809ee 100644 --- a/include/xrpl/tx/transactors/vault/VaultClawback.h +++ b/include/xrpl/tx/transactors/vault/VaultClawback.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( @@ -39,8 +36,8 @@ public: private: Expected, TER> assetsToClawback( - std::shared_ptr const& vault, - std::shared_ptr const& sleShareIssuance, + SLE::ref vault, + SLE::const_ref sleShareIssuance, AccountID const& holder, STAmount const& clawbackAmount); }; diff --git a/include/xrpl/tx/transactors/vault/VaultCreate.h b/include/xrpl/tx/transactors/vault/VaultCreate.h index bbe80b49c1..9b11f97957 100644 --- a/include/xrpl/tx/transactors/vault/VaultCreate.h +++ b/include/xrpl/tx/transactors/vault/VaultCreate.h @@ -29,10 +29,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultDelete.h b/include/xrpl/tx/transactors/vault/VaultDelete.h index d8fccd6024..b8bb3c4096 100644 --- a/include/xrpl/tx/transactors/vault/VaultDelete.h +++ b/include/xrpl/tx/transactors/vault/VaultDelete.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultDeposit.h b/include/xrpl/tx/transactors/vault/VaultDeposit.h index f1a81c928e..523b3f2e53 100644 --- a/include/xrpl/tx/transactors/vault/VaultDeposit.h +++ b/include/xrpl/tx/transactors/vault/VaultDeposit.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultSet.h b/include/xrpl/tx/transactors/vault/VaultSet.h index 48178f5048..5c362d5db4 100644 --- a/include/xrpl/tx/transactors/vault/VaultSet.h +++ b/include/xrpl/tx/transactors/vault/VaultSet.h @@ -26,10 +26,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/include/xrpl/tx/transactors/vault/VaultWithdraw.h b/include/xrpl/tx/transactors/vault/VaultWithdraw.h index 7461752ff2..7bbe06187d 100644 --- a/include/xrpl/tx/transactors/vault/VaultWithdraw.h +++ b/include/xrpl/tx/transactors/vault/VaultWithdraw.h @@ -23,10 +23,7 @@ public: doApply() override; void - visitInvariantEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) override; + visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override; [[nodiscard]] bool finalizeInvariants( diff --git a/src/libxrpl/ledger/ApplyStateTable.cpp b/src/libxrpl/ledger/ApplyStateTable.cpp index 70fa0aef5d..abcbaac0aa 100644 --- a/src/libxrpl/ledger/ApplyStateTable.cpp +++ b/src/libxrpl/ledger/ApplyStateTable.cpp @@ -80,11 +80,9 @@ ApplyStateTable::size() const void ApplyStateTable::visit( ReadView const& to, - std::function const& before, - std::shared_ptr const& after)> const& func) const + std::function< + void(uint256 const& key, bool isDelete, SLE::const_ref before, SLE::const_ref after)> const& + func) const { for (auto& item : items_) { @@ -339,7 +337,7 @@ ApplyStateTable::succ( return next; } -std::shared_ptr +SLE::const_pointer ApplyStateTable::read(ReadView const& base, Keylet const& k) const { auto const iter = items_.find(k.key); @@ -361,7 +359,7 @@ ApplyStateTable::read(ReadView const& base, Keylet const& k) const return sle; } -std::shared_ptr +SLE::pointer ApplyStateTable::peek(ReadView const& base, Keylet const& k) { auto iter = items_.lower_bound(k.key); @@ -396,7 +394,7 @@ ApplyStateTable::peek(ReadView const& base, Keylet const& k) } void -ApplyStateTable::erase(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::erase(ReadView const& base, SLE::ref sle) { auto const iter = items_.find(sle->key()); if (iter == items_.end()) @@ -420,7 +418,7 @@ ApplyStateTable::erase(ReadView const& base, std::shared_ptr const& sle) } void -ApplyStateTable::rawErase(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::rawErase(ReadView const& base, SLE::ref sle) { using namespace std; auto const result = items_.emplace( @@ -445,7 +443,7 @@ ApplyStateTable::rawErase(ReadView const& base, std::shared_ptr const& sle) } void -ApplyStateTable::insert(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::insert(ReadView const& base, SLE::ref sle) { auto const iter = items_.lower_bound(sle->key()); if (iter == items_.end() || iter->first != sle->key()) @@ -475,7 +473,7 @@ ApplyStateTable::insert(ReadView const& base, std::shared_ptr const& sle) } void -ApplyStateTable::replace(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::replace(ReadView const& base, SLE::ref sle) { auto const iter = items_.lower_bound(sle->key()); if (iter == items_.end() || iter->first != sle->key()) @@ -504,7 +502,7 @@ ApplyStateTable::replace(ReadView const& base, std::shared_ptr const& sle) } void -ApplyStateTable::update(ReadView const& base, std::shared_ptr const& sle) +ApplyStateTable::update(ReadView const& base, SLE::ref sle) { auto const iter = items_.find(sle->key()); if (iter == items_.end()) @@ -536,7 +534,7 @@ ApplyStateTable::destroyXRP(XRPAmount const& fee) // Insert this transaction to the SLE's threading list void -ApplyStateTable::threadItem(TxMeta& meta, std::shared_ptr const& sle) +ApplyStateTable::threadItem(TxMeta& meta, SLE::ref sle) { key_type prevTxID; LedgerIndex prevLgrID = 0; @@ -568,7 +566,7 @@ ApplyStateTable::threadItem(TxMeta& meta, std::shared_ptr const& sle) } } -std::shared_ptr +SLE::pointer ApplyStateTable::getForMod(ReadView const& base, key_type const& key, Mods& mods, beast::Journal j) { { @@ -640,7 +638,7 @@ void ApplyStateTable::threadOwners( ReadView const& base, TxMeta& meta, - std::shared_ptr const& sle, + SLE::const_ref sle, Mods& mods, beast::Journal j) { diff --git a/src/libxrpl/ledger/ApplyView.cpp b/src/libxrpl/ledger/ApplyView.cpp index 2200afecac..8575bc2f52 100644 --- a/src/libxrpl/ledger/ApplyView.cpp +++ b/src/libxrpl/ledger/ApplyView.cpp @@ -31,7 +31,7 @@ createRoot( ApplyView& view, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe) + std::function const& describe) { auto newRoot = std::make_shared(directory); newRoot->setFieldH256(sfRootIndex, directory.key); @@ -110,7 +110,7 @@ insertPage( SLE::ref next, uint256 const& key, Keylet const& directory, - std::function const&)> const& describe) + std::function const& describe) { // We rely on modulo arithmetic of unsigned integers (guaranteed in // [basic.fundamental] paragraph 2) to detect page representation overflow. @@ -166,7 +166,7 @@ ApplyView::dirAdd( bool preserveOrder, Keylet const& directory, uint256 const& key, - std::function const&)> const& describe) + std::function const& describe) { auto root = peek(directory); diff --git a/src/libxrpl/ledger/ApplyViewBase.cpp b/src/libxrpl/ledger/ApplyViewBase.cpp index e5a8e11b4c..1b6ba78046 100644 --- a/src/libxrpl/ledger/ApplyViewBase.cpp +++ b/src/libxrpl/ledger/ApplyViewBase.cpp @@ -58,7 +58,7 @@ ApplyViewBase::succ(key_type const& key, std::optional const& last) co return items_.succ(*base_, key, last); } -std::shared_ptr +SLE::const_pointer ApplyViewBase::read(Keylet const& k) const { return items_.read(*base_, k); @@ -114,26 +114,26 @@ ApplyViewBase::flags() const return flags_; } -std::shared_ptr +SLE::pointer ApplyViewBase::peek(Keylet const& k) { return items_.peek(*base_, k); } void -ApplyViewBase::erase(std::shared_ptr const& sle) +ApplyViewBase::erase(SLE::ref sle) { items_.erase(*base_, sle); } void -ApplyViewBase::insert(std::shared_ptr const& sle) +ApplyViewBase::insert(SLE::ref sle) { items_.insert(*base_, sle); } void -ApplyViewBase::update(std::shared_ptr const& sle) +ApplyViewBase::update(SLE::ref sle) { items_.update(*base_, sle); } @@ -141,19 +141,19 @@ ApplyViewBase::update(std::shared_ptr const& sle) //--- void -ApplyViewBase::rawErase(std::shared_ptr const& sle) +ApplyViewBase::rawErase(SLE::ref sle) { items_.rawErase(*base_, sle); } void -ApplyViewBase::rawInsert(std::shared_ptr const& sle) +ApplyViewBase::rawInsert(SLE::ref sle) { items_.insert(*base_, sle); } void -ApplyViewBase::rawReplace(std::shared_ptr const& sle) +ApplyViewBase::rawReplace(SLE::ref sle) { items_.replace(*base_, sle); } diff --git a/src/libxrpl/ledger/ApplyViewImpl.cpp b/src/libxrpl/ledger/ApplyViewImpl.cpp index 9650190a3e..66c009cd88 100644 --- a/src/libxrpl/ledger/ApplyViewImpl.cpp +++ b/src/libxrpl/ledger/ApplyViewImpl.cpp @@ -13,7 +13,6 @@ #include #include -#include #include namespace xrpl { @@ -43,11 +42,9 @@ ApplyViewImpl::size() void ApplyViewImpl::visit( OpenView& to, - std::function const& before, - std::shared_ptr const& after)> const& func) + std::function< + void(uint256 const& key, bool isDelete, SLE::const_ref before, SLE::const_ref after)> const& + func) { items_.visit(to, func); } diff --git a/src/libxrpl/ledger/CachedView.cpp b/src/libxrpl/ledger/CachedView.cpp index 8a6c266b8f..1853cc0ac8 100644 --- a/src/libxrpl/ledger/CachedView.cpp +++ b/src/libxrpl/ledger/CachedView.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -19,7 +18,7 @@ CachedViewImpl::exists(Keylet const& k) const return read(k) != nullptr; } -std::shared_ptr +SLE::const_pointer CachedViewImpl::read(Keylet const& k) const { static CountedObjects::Counter kHits{"CachedView::hit"}; diff --git a/src/libxrpl/ledger/Ledger.cpp b/src/libxrpl/ledger/Ledger.cpp index fe7db9a158..82956650e2 100644 --- a/src/libxrpl/ledger/Ledger.cpp +++ b/src/libxrpl/ledger/Ledger.cpp @@ -401,7 +401,7 @@ Ledger::succ(uint256 const& key, std::optional const& last) const return item->key(); } -std::shared_ptr +SLE::const_pointer Ledger::read(Keylet const& k) const { if (k.key == beast::kZero) @@ -486,7 +486,7 @@ Ledger::digest(key_type const& key) const -> std::optional //------------------------------------------------------------------------------ void -Ledger::rawErase(std::shared_ptr const& sle) +Ledger::rawErase(SLE::ref sle) { if (!stateMap_.delItem(sle->key())) logicError("Ledger::rawErase: key not found"); @@ -500,7 +500,7 @@ Ledger::rawErase(uint256 const& key) } void -Ledger::rawInsert(std::shared_ptr const& sle) +Ledger::rawInsert(SLE::ref sle) { Serializer ss; sle->add(ss); @@ -512,7 +512,7 @@ Ledger::rawInsert(std::shared_ptr const& sle) } void -Ledger::rawReplace(std::shared_ptr const& sle) +Ledger::rawReplace(SLE::ref sle) { Serializer ss; sle->add(ss); @@ -623,7 +623,7 @@ Ledger::setup() return ret; } -std::shared_ptr +SLE::pointer Ledger::peek(Keylet const& k) const { auto const& value = stateMap_.peekItem(k.key); diff --git a/src/libxrpl/ledger/OpenView.cpp b/src/libxrpl/ledger/OpenView.cpp index 40b411fcc0..26cb109f85 100644 --- a/src/libxrpl/ledger/OpenView.cpp +++ b/src/libxrpl/ledger/OpenView.cpp @@ -163,7 +163,7 @@ OpenView::succ(key_type const& key, std::optional const& last) const return items_.succ(*base_, key, last); } -std::shared_ptr +SLE::const_pointer OpenView::read(Keylet const& k) const { return items_.read(*base_, k); @@ -228,19 +228,19 @@ OpenView::txRead(key_type const& key) const -> tx_type //--- void -OpenView::rawErase(std::shared_ptr const& sle) +OpenView::rawErase(SLE::ref sle) { items_.erase(sle); } void -OpenView::rawInsert(std::shared_ptr const& sle) +OpenView::rawInsert(SLE::ref sle) { items_.insert(sle); } void -OpenView::rawReplace(std::shared_ptr const& sle) +OpenView::rawReplace(SLE::ref sle) { items_.replace(sle); } diff --git a/src/libxrpl/ledger/RawStateTable.cpp b/src/libxrpl/ledger/RawStateTable.cpp index 69e2f5c0fe..fbe7bf1d82 100644 --- a/src/libxrpl/ledger/RawStateTable.cpp +++ b/src/libxrpl/ledger/RawStateTable.cpp @@ -20,10 +20,10 @@ namespace xrpl::detail { class RawStateTable::SlesIterImpl : public ReadView::SlesType::iter_base { private: - std::shared_ptr sle0_; + SLE::const_pointer sle0_; ReadView::SlesType::Iterator iter0_; ReadView::SlesType::Iterator end0_; - std::shared_ptr sle1_; + SLE::const_pointer sle1_; items_t::const_iterator iter1_; items_t::const_iterator end1_; @@ -241,7 +241,7 @@ RawStateTable::succ(ReadView const& base, key_type const& key, std::optional const& sle) +RawStateTable::erase(SLE::ref sle) { // The base invariant is checked during apply auto const result = items_.emplace( @@ -267,7 +267,7 @@ RawStateTable::erase(std::shared_ptr const& sle) } void -RawStateTable::insert(std::shared_ptr const& sle) +RawStateTable::insert(SLE::ref sle) { auto const result = items_.emplace( std::piecewise_construct, @@ -292,7 +292,7 @@ RawStateTable::insert(std::shared_ptr const& sle) } void -RawStateTable::replace(std::shared_ptr const& sle) +RawStateTable::replace(SLE::ref sle) { auto const result = items_.emplace( std::piecewise_construct, @@ -313,7 +313,7 @@ RawStateTable::replace(std::shared_ptr const& sle) } } -std::shared_ptr +SLE::const_pointer RawStateTable::read(ReadView const& base, Keylet const& k) const { auto const iter = items_.find(k.key); @@ -323,7 +323,7 @@ RawStateTable::read(ReadView const& base, Keylet const& k) const if (item.action == Action::Erase) return nullptr; // Convert to SLE const - std::shared_ptr sle = item.sle; + SLE::const_pointer sle = item.sle; if (!k.check(*sle)) return nullptr; return sle; diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index c62d79dcac..fdd7998609 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -32,7 +32,6 @@ #include #include -#include #include #include @@ -332,11 +331,7 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal) //------------------------------------------------------------------------------ TER -dirLink( - ApplyView& view, - AccountID const& owner, - std::shared_ptr& object, - SF_UINT64 const& node) +dirLink(ApplyView& view, AccountID const& owner, SLE::pointer& object, SF_UINT64 const& node) { auto const page = view.dirInsert(keylet::ownerDir(owner), object->key(), describeOwnerDir(owner)); @@ -488,7 +483,7 @@ cleanupOnAccountDelete( std::optional maxNodesToDelete) { // Delete all the entries in the account directory. - std::shared_ptr sleDirNode{}; + SLE::pointer sleDirNode{}; unsigned int uDirEntry{0}; uint256 dirEntry{beast::kZero}; std::uint32_t deleted = 0; diff --git a/src/libxrpl/ledger/helpers/AMMHelpers.cpp b/src/libxrpl/ledger/helpers/AMMHelpers.cpp index f7aabc8ea5..fe6d022490 100644 --- a/src/libxrpl/ledger/helpers/AMMHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AMMHelpers.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -636,7 +635,7 @@ deleteAMMTrustLines( keylet::ownerDir(ammAccountID), [&](LedgerEntryType nodeType, uint256 const&, - std::shared_ptr& sleItem) -> std::pair { + SLE::pointer& sleItem) -> std::pair { // Skip AMM and MPToken if (nodeType == ltAMM || nodeType == ltMPTOKEN) return {tesSUCCESS, SkipEntry::Yes}; @@ -672,7 +671,7 @@ deleteAMMMPTokens(Sandbox& sb, AccountID const& ammAccountID, beast::Journal j) keylet::ownerDir(ammAccountID), [&](LedgerEntryType nodeType, uint256 const&, - std::shared_ptr& sleItem) -> std::pair { + SLE::pointer& sleItem) -> std::pair { // Skip AMM if (nodeType == ltAMM) return {tesSUCCESS, SkipEntry::Yes}; @@ -768,7 +767,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo void initializeFeeAuctionVote( ApplyView& view, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account, Asset const& lptAsset, std::uint16_t tfee) @@ -926,7 +925,7 @@ Expected verifyAndAdjustLPTokenBalance( Sandbox& sb, STAmount const& lpTokens, - std::shared_ptr& ammSle, + SLE::pointer& ammSle, AccountID const& account) { auto const res = isOnlyLiquidityProvider(sb, lpTokens.get(), account); diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 029cb5cd92..1634de93c9 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -125,11 +125,7 @@ transferRate(ReadView const& view, AccountID const& issuer) } void -adjustOwnerCount( - ApplyView& view, - std::shared_ptr const& sle, - std::int32_t amount, - beast::Journal j) +adjustOwnerCount(ApplyView& view, SLE::ref sle, std::int32_t amount, beast::Journal j) { if (!sle) return; @@ -192,9 +188,7 @@ getPseudoAccountFields() } [[nodiscard]] bool -isPseudoAccount( - std::shared_ptr sleAcct, - std::set const& pseudoFieldFilter) +isPseudoAccount(SLE::const_pointer sleAcct, std::set const& pseudoFieldFilter) { auto const& fields = getPseudoAccountFields(); @@ -208,7 +202,7 @@ isPseudoAccount( }) > 0; } -Expected, TER> +Expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField) { [[maybe_unused]] diff --git a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp index 838e72d364..28b50b51d6 100644 --- a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -71,7 +70,7 @@ removeExpired(ApplyView& view, STVector256 const& arr, beast::Journal const j) } TER -deleteSLE(ApplyView& view, std::shared_ptr const& sleCredential, beast::Journal j) +deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j) { if (!sleCredential) return tecNO_ENTRY; @@ -231,7 +230,7 @@ TER authorizedDepositPreauth(ReadView const& view, STVector256 const& credIDs, AccountID const& dst) { std::set> sorted; - std::vector> lifeExtender; + std::vector lifeExtender; lifeExtender.reserve(credIDs.size()); for (auto const& h : credIDs) { @@ -352,7 +351,7 @@ verifyDepositPreauth( ApplyView& view, AccountID const& src, AccountID const& dst, - std::shared_ptr const& sleDst, + SLE::const_ref sleDst, beast::Journal j) { // If depositPreauth is enabled, then an account that requires diff --git a/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp b/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp index 8b4eeae7b7..43f8219b6a 100644 --- a/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp +++ b/src/libxrpl/ledger/helpers/DirectoryHelpers.cpp @@ -13,15 +13,13 @@ #include #include -#include - namespace xrpl { bool dirFirst( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry) { @@ -32,7 +30,7 @@ bool dirNext( ApplyView& view, uint256 const& root, - std::shared_ptr& page, + SLE::pointer& page, unsigned int& index, uint256& entry) { @@ -43,7 +41,7 @@ bool cdirFirst( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry) { @@ -54,7 +52,7 @@ bool cdirNext( ReadView const& view, uint256 const& root, - std::shared_ptr& page, + SLE::const_pointer& page, unsigned int& index, uint256& entry) { @@ -62,10 +60,7 @@ cdirNext( } void -forEachItem( - ReadView const& view, - Keylet const& root, - std::function const&)> const& f) +forEachItem(ReadView const& view, Keylet const& root, std::function const& f) { XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItem : valid root type"); @@ -95,7 +90,7 @@ forEachItemAfter( uint256 const& after, std::uint64_t const hint, unsigned int limit, - std::function const&)> const& f) + std::function const& f) { XRPL_ASSERT(root.type == ltDIR_NODE, "xrpl::forEachItemAfter : valid root type"); @@ -184,7 +179,7 @@ dirIsEmpty(ReadView const& view, Keylet const& k) std::function describeOwnerDir(AccountID const& account) { - return [account](std::shared_ptr const& sle) { (*sle)[sfOwner] = account; }; + return [account](SLE::ref sle) { (*sle)[sfOwner] = account; }; } } // namespace xrpl diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index bb784278ba..5b467db796 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -41,7 +41,7 @@ namespace xrpl::nft { -static std::shared_ptr +static SLE::const_pointer locatePage(ReadView const& view, AccountID const& owner, uint256 const& id) { auto const first = keylet::nftpage(keylet::nftpageMin(owner), id); @@ -54,7 +54,7 @@ locatePage(ReadView const& view, AccountID const& owner, uint256 const& id) Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); } -static std::shared_ptr +static SLE::pointer locatePage(ApplyView& view, AccountID const& owner, uint256 const& id) { auto const first = keylet::nftpage(keylet::nftpageMin(owner), id); @@ -67,7 +67,7 @@ locatePage(ApplyView& view, AccountID const& owner, uint256 const& id) Keylet(ltNFTOKEN_PAGE, view.succ(first.key, last.key.next()).value_or(last.key))); } -static std::shared_ptr +static SLE::pointer getPageForToken( ApplyView& view, AccountID const& owner, @@ -230,7 +230,7 @@ changeTokenURI( uint256 const& nftokenID, std::optional const& uri) { - std::shared_ptr const page = locatePage(view, owner, nftokenID); + SLE::pointer const page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) @@ -267,7 +267,7 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft) // First, we need to locate the page the NFT belongs to, creating it // if necessary. This operation may fail if it is impossible to insert // the NFT. - std::shared_ptr const page = + SLE::pointer const page = getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) { adjustOwnerCount( view, @@ -296,7 +296,7 @@ insertToken(ApplyView& view, AccountID owner, STObject&& nft) } static bool -mergePages(ApplyView& view, std::shared_ptr const& p1, std::shared_ptr const& p2) +mergePages(ApplyView& view, SLE::ref p1, SLE::ref p2) { if (p1->key() >= p2->key()) Throw("mergePages: pages passed in out of order!"); @@ -355,7 +355,7 @@ mergePages(ApplyView& view, std::shared_ptr const& p1, std::shared_ptr TER removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) { - std::shared_ptr const page = locatePage(view, owner, nftokenID); + SLE::pointer const page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) @@ -366,11 +366,7 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) /** Remove the token from the owner's token directory. */ TER -removeToken( - ApplyView& view, - AccountID const& owner, - uint256 const& nftokenID, - std::shared_ptr const& curr) +removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, SLE::ref curr) { // We found a page, but the given NFT may not be in it. auto arr = curr->getFieldArray(sfNFTokens); @@ -386,8 +382,8 @@ removeToken( } // Page management: - auto const loadPage = [&view](std::shared_ptr const& page1, SF_UINT256 const& field) { - std::shared_ptr page2; + auto const loadPage = [&view](SLE::ref page1, SF_UINT256 const& field) { + SLE::pointer page2; if (auto const id = (*page1)[~field]) { @@ -535,7 +531,7 @@ removeToken( std::optional findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID) { - std::shared_ptr const page = locatePage(view, owner, nftokenID); + SLE::const_pointer const page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) @@ -554,7 +550,7 @@ findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID std::optional findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftokenID) { - std::shared_ptr page = locatePage(view, owner, nftokenID); + SLE::pointer page = locatePage(view, owner, nftokenID); // If the page couldn't be found, the given NFT isn't owned by this account if (!page) @@ -623,7 +619,7 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t } bool -deleteTokenOffer(ApplyView& view, std::shared_ptr const& offer) +deleteTokenOffer(ApplyView& view, SLE::ref offer) { if (offer->getType() != ltNFTOKEN_OFFER) return false; @@ -657,7 +653,7 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) auto const last = keylet::nftpageMax(owner); - std::shared_ptr page = view.peek(Keylet( + SLE::pointer page = view.peek(Keylet( ltNFTOKEN_PAGE, view.succ(keylet::nftpageMin(owner).key, last.key.next()).value_or(last.key))); @@ -691,7 +687,7 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner) view.update(page); } - std::shared_ptr nextPage; + SLE::pointer nextPage; while ( (nextPage = view.peek(Keylet( ltNFTOKEN_PAGE, view.succ(page->key().next(), last.key.next()).value_or(last.key))))) @@ -956,7 +952,7 @@ tokenOfferCreateApply( auto const offerNode = view.dirInsert( isSellOffer ? keylet::nftSells(nftokenID) : keylet::nftBuys(nftokenID), offerID, - [&nftokenID, isSellOffer](std::shared_ptr const& sle) { + [&nftokenID, isSellOffer](SLE::ref sle) { (*sle)[sfFlags] = isSellOffer ? lsfNFTokenSellOffers : lsfNFTokenBuyOffers; (*sle)[sfNFTokenID] = nftokenID; }); diff --git a/src/libxrpl/ledger/helpers/OfferHelpers.cpp b/src/libxrpl/ledger/helpers/OfferHelpers.cpp index 03a1170aad..5249870143 100644 --- a/src/libxrpl/ledger/helpers/OfferHelpers.cpp +++ b/src/libxrpl/ledger/helpers/OfferHelpers.cpp @@ -12,12 +12,10 @@ #include #include -#include - namespace xrpl { TER -offerDelete(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) +offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j) { if (!sle) return tesSUCCESS; diff --git a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp index 31c206d85b..e755dbaca3 100644 --- a/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp +++ b/src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp @@ -12,16 +12,10 @@ #include #include -#include - namespace xrpl { TER -closeChannel( - std::shared_ptr const& slep, - ApplyView& view, - uint256 const& key, - beast::Journal j) +closeChannel(SLE::ref slep, ApplyView& view, uint256 const& key, beast::Journal j) { AccountID const src = (*slep)[sfAccount]; // Remove PayChan from owner directory diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index 5aaa417ad9..7ed7ab8fc4 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -294,7 +294,7 @@ trustCreate( TER trustDelete( ApplyView& view, - std::shared_ptr const& sleRippleState, + SLE::ref sleRippleState, AccountID const& uLowAccountID, AccountID const& uHighAccountID, beast::Journal j) @@ -738,7 +738,7 @@ removeEmptyHolding( TER deleteAMMTrustLine( ApplyView& view, - std::shared_ptr sleState, + SLE::pointer sleState, std::optional const& ammAccountID, beast::Journal j) { @@ -786,7 +786,7 @@ deleteAMMTrustLine( TER deleteAMMMPToken( ApplyView& view, - std::shared_ptr sleMpt, + SLE::pointer sleMpt, AccountID const& ammAccountID, beast::Journal j) { diff --git a/src/libxrpl/ledger/helpers/VaultHelpers.cpp b/src/libxrpl/ledger/helpers/VaultHelpers.cpp index 587923953d..3a3a756499 100644 --- a/src/libxrpl/ledger/helpers/VaultHelpers.cpp +++ b/src/libxrpl/ledger/helpers/VaultHelpers.cpp @@ -12,16 +12,12 @@ #include // IWYU pragma: keep #include -#include #include namespace xrpl { [[nodiscard]] std::optional -assetsToSharesDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& assets) +assetsToSharesDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& assets) { XRPL_ASSERT(!assets.negative(), "xrpl::assetsToSharesDeposit : non-negative assets"); XRPL_ASSERT( @@ -45,10 +41,7 @@ assetsToSharesDeposit( } [[nodiscard]] std::optional -sharesToAssetsDeposit( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, - STAmount const& shares) +sharesToAssetsDeposit(SLE::const_ref vault, SLE::const_ref issuance, STAmount const& shares) { XRPL_ASSERT(!shares.negative(), "xrpl::sharesToAssetsDeposit : non-negative shares"); XRPL_ASSERT( @@ -72,8 +65,8 @@ sharesToAssetsDeposit( [[nodiscard]] std::optional assetsToSharesWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, + SLE::const_ref vault, + SLE::const_ref issuance, STAmount const& assets, TruncateShares truncate, WaiveUnrealizedLoss waive) @@ -101,8 +94,8 @@ assetsToSharesWithdraw( [[nodiscard]] std::optional sharesToAssetsWithdraw( - std::shared_ptr const& vault, - std::shared_ptr const& issuance, + SLE::const_ref vault, + SLE::const_ref issuance, STAmount const& shares, WaiveUnrealizedLoss waive) { diff --git a/src/libxrpl/tx/ApplyContext.cpp b/src/libxrpl/tx/ApplyContext.cpp index 88e37baf00..93b0d101af 100644 --- a/src/libxrpl/tx/ApplyContext.cpp +++ b/src/libxrpl/tx/ApplyContext.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -70,11 +69,7 @@ ApplyContext::size() void ApplyContext::visit( - std::function const&, - std::shared_ptr const&)> const& func) + std::function const& func) { view_->visit(base_, func); // NOLINT(bugprone-unchecked-optional-access) } @@ -104,13 +99,11 @@ ApplyContext::checkInvariantsHelper( auto checkers = getInvariantChecks(); // call each check's per-entry method - visit([&checkers]( - uint256 const& index, - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) { - (..., std::get(checkers).visitEntry(isDelete, before, after)); - }); + visit( + [&checkers]( + uint256 const& index, bool isDelete, SLE::const_ref before, SLE::const_ref after) { + (..., std::get(checkers).visitEntry(isDelete, before, after)); + }); // Note: do not replace this logic with a `...&&` fold expression. // The fold expression will only run until the first check fails (it diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 28fa059902..51541cc2e3 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -789,7 +788,7 @@ Transactor::checkSingleSign( ReadView const& view, AccountID const& idSigner, AccountID const& idAccount, - std::shared_ptr sleAccount, + SLE::const_pointer sleAccount, beast::Journal const j) { bool const isMasterDisabled = sleAccount->isFlag(lsfDisableMaster); @@ -825,7 +824,7 @@ Transactor::checkMultiSign( beast::Journal const j) { // Get id's SignerList and Quorum. - std::shared_ptr const sleAccountSigners = view.read(keylet::signers(id)); + STLedgerEntry::const_pointer const sleAccountSigners = view.read(keylet::signers(id)); // If the signer list doesn't exist the account is not multi-signing. if (!sleAccountSigners) { @@ -1286,8 +1285,8 @@ Transactor::operator()() &expiredCredentials]( uint256 const& index, bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) { + SLE::const_ref before, + SLE::const_ref after) { if (isDelete) { XRPL_ASSERT( diff --git a/src/libxrpl/tx/invariants/AMMInvariant.cpp b/src/libxrpl/tx/invariants/AMMInvariant.cpp index be2a803e93..ecd7bedf89 100644 --- a/src/libxrpl/tx/invariants/AMMInvariant.cpp +++ b/src/libxrpl/tx/invariants/AMMInvariant.cpp @@ -19,16 +19,12 @@ #include #include -#include #include namespace xrpl { void -ValidAMM::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidAMM::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (isDelete) return; diff --git a/src/libxrpl/tx/invariants/FreezeInvariant.cpp b/src/libxrpl/tx/invariants/FreezeInvariant.cpp index eaaeb7fc6f..0293d42e97 100644 --- a/src/libxrpl/tx/invariants/FreezeInvariant.cpp +++ b/src/libxrpl/tx/invariants/FreezeInvariant.cpp @@ -16,16 +16,12 @@ #include #include -#include #include namespace xrpl { void -TransfersNotFrozen::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +TransfersNotFrozen::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { /* * A trust line freeze state alone doesn't determine if a transfer is @@ -107,9 +103,7 @@ TransfersNotFrozen::finalize( } bool -TransfersNotFrozen::isValidEntry( - std::shared_ptr const& before, - std::shared_ptr const& after) +TransfersNotFrozen::isValidEntry(SLE::const_ref before, SLE::const_ref after) { // `after` can never be null, even if the trust line is deleted. XRPL_ASSERT(after, "xrpl::TransfersNotFrozen::isValidEntry : valid after."); @@ -135,8 +129,8 @@ TransfersNotFrozen::isValidEntry( STAmount TransfersNotFrozen::calculateBalanceChange( - std::shared_ptr const& before, - std::shared_ptr const& after, + SLE::const_ref before, + SLE::const_ref after, bool isDelete) { auto const getBalance = [](auto const& line, auto const& other, bool zero) { @@ -180,9 +174,7 @@ TransfersNotFrozen::recordBalance(Issue const& issue, BalanceChange change) } void -TransfersNotFrozen::recordBalanceChanges( - std::shared_ptr const& after, - STAmount const& balanceChange) +TransfersNotFrozen::recordBalanceChanges(SLE::const_ref after, STAmount const& balanceChange) { auto const balanceChangeSign = balanceChange.signum(); auto const currency = after->at(sfBalance).get().currency; @@ -198,7 +190,7 @@ TransfersNotFrozen::recordBalanceChanges( {.line = after, .balanceChangeSign = -balanceChangeSign}); } -std::shared_ptr +SLE::const_pointer TransfersNotFrozen::findIssuer(AccountID const& issuerID, ReadView const& view) { if (auto it = possibleIssuers_.find(issuerID); it != possibleIssuers_.end()) @@ -211,7 +203,7 @@ TransfersNotFrozen::findIssuer(AccountID const& issuerID, ReadView const& view) bool TransfersNotFrozen::validateIssuerChanges( - std::shared_ptr const& issuer, + SLE::const_ref issuer, IssuerChanges const& changes, STTx const& tx, beast::Journal const& j, diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 0154dca747..b4a533905c 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -64,10 +64,7 @@ hasPrivilege(STTx const& tx, Privilege priv) #pragma pop_macro("TRANSACTION") void -TransactionFeeCheck::visitEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +TransactionFeeCheck::visitEntry(bool, SLE::const_ref, SLE::const_ref) { // nothing to do } @@ -110,10 +107,7 @@ TransactionFeeCheck::finalize( //------------------------------------------------------------------------------ void -XRPNotCreated::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +XRPNotCreated::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { /* We go through all modified ledger entries, looking only at account roots, * escrow payments, and payment channels. We remove from the total any @@ -192,10 +186,7 @@ XRPNotCreated::finalize( //------------------------------------------------------------------------------ void -XRPBalanceChecks::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +XRPBalanceChecks::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { auto isBad = [](STAmount const& balance) { if (!balance.native()) @@ -242,10 +233,7 @@ XRPBalanceChecks::finalize( //------------------------------------------------------------------------------ void -NoBadOffers::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +NoBadOffers::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { auto isBad = [](STAmount const& pays, STAmount const& gets) { // An offer should never be negative @@ -286,10 +274,7 @@ NoBadOffers::finalize( //------------------------------------------------------------------------------ void -NoZeroEscrow::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +NoZeroEscrow::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { auto isBad = [](STAmount const& amount) { // XRP case @@ -393,10 +378,7 @@ NoZeroEscrow::finalize( //------------------------------------------------------------------------------ void -AccountRootsNotDeleted::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const&) +AccountRootsNotDeleted::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref) { if (isDelete && before && before->getType() == ltACCOUNT_ROOT) accountsDeleted_++; @@ -446,10 +428,7 @@ AccountRootsNotDeleted::finalize( //------------------------------------------------------------------------------ void -AccountRootsDeletedClean::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +AccountRootsDeletedClean::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (isDelete && before && before->getType() == ltACCOUNT_ROOT) accountsDeleted_.emplace_back(before, after); @@ -566,10 +545,7 @@ AccountRootsDeletedClean::finalize( //------------------------------------------------------------------------------ void -LedgerEntryTypesMatch::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +LedgerEntryTypesMatch::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { if (before && after && before->getType() != after->getType()) typeMismatch_ = true; @@ -623,10 +599,7 @@ LedgerEntryTypesMatch::finalize( //------------------------------------------------------------------------------ void -NoXRPTrustLines::visitEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const& after) +NoXRPTrustLines::visitEntry(bool, SLE::const_ref, SLE::const_ref after) { bool const overwriteFixEnabled = isFeatureEnabled(fixCleanup3_1_3, true); @@ -666,10 +639,7 @@ NoXRPTrustLines::finalize( //------------------------------------------------------------------------------ void -NoDeepFreezeTrustLinesWithoutFreeze::visitEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const& after) +NoDeepFreezeTrustLinesWithoutFreeze::visitEntry(bool, SLE::const_ref, SLE::const_ref after) { if (after && after->getType() == ltRIPPLE_STATE) { @@ -712,10 +682,7 @@ NoDeepFreezeTrustLinesWithoutFreeze::finalize( //------------------------------------------------------------------------------ void -ValidNewAccountRoot::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidNewAccountRoot::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { if (!before && after->getType() == ltACCOUNT_ROOT) { @@ -789,10 +756,7 @@ ValidNewAccountRoot::finalize( //------------------------------------------------------------------------------ void -ValidClawback::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const&) +ValidClawback::visitEntry(bool, SLE::const_ref before, SLE::const_ref) { if (before && before->getType() == ltRIPPLE_STATE) trustlinesChanged_++; @@ -877,10 +841,7 @@ ValidClawback::finalize( //------------------------------------------------------------------------------ void -ValidPseudoAccounts::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidPseudoAccounts::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (isDelete) { @@ -968,10 +929,7 @@ ValidPseudoAccounts::finalize( //------------------------------------------------------------------------------ void -NoModifiedUnmodifiableFields::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +NoModifiedUnmodifiableFields::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (isDelete || !before) { diff --git a/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp b/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp index 56995ef94c..8586a27be3 100644 --- a/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp +++ b/src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp @@ -15,15 +15,10 @@ #include #include -#include - namespace xrpl { void -ValidLoanBroker::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidLoanBroker::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (after) { diff --git a/src/libxrpl/tx/invariants/LoanInvariant.cpp b/src/libxrpl/tx/invariants/LoanInvariant.cpp index 3eef0957e5..ce9a7c6e03 100644 --- a/src/libxrpl/tx/invariants/LoanInvariant.cpp +++ b/src/libxrpl/tx/invariants/LoanInvariant.cpp @@ -12,15 +12,10 @@ #include #include -#include - namespace xrpl { void -ValidLoan::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidLoan::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (after && after->getType() == ltLOAN) { diff --git a/src/libxrpl/tx/invariants/MPTInvariant.cpp b/src/libxrpl/tx/invariants/MPTInvariant.cpp index 635af25b61..a2369cc1b9 100644 --- a/src/libxrpl/tx/invariants/MPTInvariant.cpp +++ b/src/libxrpl/tx/invariants/MPTInvariant.cpp @@ -24,14 +24,10 @@ #include #include #include - namespace xrpl { void -ValidMPTIssuance::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidMPTIssuance::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { // The sfReferenceHolding tracking and the deleted-holding capture are // only meaningful post-fixCleanup3_2_0 (the field is never set @@ -369,10 +365,7 @@ ValidMPTIssuance::finalize( } void -ValidMPTPayment::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidMPTPayment::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { if (overflow_) return; diff --git a/src/libxrpl/tx/invariants/NFTInvariant.cpp b/src/libxrpl/tx/invariants/NFTInvariant.cpp index 09ae9db925..52ecbcd9d1 100644 --- a/src/libxrpl/tx/invariants/NFTInvariant.cpp +++ b/src/libxrpl/tx/invariants/NFTInvariant.cpp @@ -20,16 +20,12 @@ #include #include -#include #include namespace xrpl { void -ValidNFTokenPage::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidNFTokenPage::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { static constexpr uint256 const& kPageBits = nft::kPageMask; static constexpr uint256 kAccountBits = ~kPageBits; @@ -38,7 +34,7 @@ ValidNFTokenPage::visitEntry( (after && after->getType() != ltNFTOKEN_PAGE)) return; - auto check = [this, isDelete](std::shared_ptr const& sle) { + auto check = [this, isDelete](SLE::const_ref sle) { uint256 const account = sle->key() & kAccountBits; uint256 const hiLimit = sle->key() & kPageBits; std::optional const prev = (*sle)[~sfPreviousPageMin]; @@ -187,10 +183,7 @@ ValidNFTokenPage::finalize( //------------------------------------------------------------------------------ void -NFTokenCountTracking::visitEntry( - bool, - std::shared_ptr const& before, - std::shared_ptr const& after) +NFTokenCountTracking::visitEntry(bool, SLE::const_ref before, SLE::const_ref after) { if (before && before->getType() == ltACCOUNT_ROOT) { diff --git a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp index 282df85302..1014642b36 100644 --- a/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDEXInvariant.cpp @@ -14,15 +14,10 @@ #include #include -#include - namespace xrpl { void -ValidPermissionedDEX::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidPermissionedDEX::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { if (after && after->getType() == ltDIR_NODE) { diff --git a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp index 93c1b2058e..544a3af2dc 100644 --- a/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp +++ b/src/libxrpl/tx/invariants/PermissionedDomainInvariant.cpp @@ -15,23 +15,19 @@ #include #include -#include #include namespace xrpl { void -ValidPermissionedDomain::visitEntry( - bool isDel, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidPermissionedDomain::visitEntry(bool isDel, SLE::const_ref before, SLE::const_ref after) { if (before && before->getType() != ltPERMISSIONED_DOMAIN) return; if (after && after->getType() != ltPERMISSIONED_DOMAIN) return; - auto check = [isDel](std::vector& sleStatus, std::shared_ptr const& sle) { + auto check = [isDel](std::vector& sleStatus, SLE::const_ref sle) { auto const& credentials = sle->getFieldArray(sfAcceptedCredentials); auto const sorted = credentials::makeSorted(credentials); diff --git a/src/libxrpl/tx/invariants/VaultInvariant.cpp b/src/libxrpl/tx/invariants/VaultInvariant.cpp index 4aa79279a1..80b8f36bd9 100644 --- a/src/libxrpl/tx/invariants/VaultInvariant.cpp +++ b/src/libxrpl/tx/invariants/VaultInvariant.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -63,10 +62,7 @@ ValidVault::Shares::make(SLE const& from) } void -ValidVault::visitEntry( - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) +ValidVault::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) { // If `before` is empty, this means an object is being created, in which // case `isDelete` must be false. Otherwise `before` and `after` are set and diff --git a/src/libxrpl/tx/paths/BookTip.cpp b/src/libxrpl/tx/paths/BookTip.cpp index 5be512aa84..eda11e5d72 100644 --- a/src/libxrpl/tx/paths/BookTip.cpp +++ b/src/libxrpl/tx/paths/BookTip.cpp @@ -8,8 +8,6 @@ #include #include -#include - namespace xrpl { BookTip::BookTip(ApplyView& view, Book const& book) @@ -40,7 +38,7 @@ BookTip::step(beast::Journal j) return false; unsigned int di = 0; - std::shared_ptr dir; + SLE::pointer dir; if (dirFirst(view_, *firstPage, dir, di, index_)) { diff --git a/src/libxrpl/tx/paths/DirectStep.cpp b/src/libxrpl/tx/paths/DirectStep.cpp index a16c3afd6a..cc6e75ea3d 100644 --- a/src/libxrpl/tx/paths/DirectStep.cpp +++ b/src/libxrpl/tx/paths/DirectStep.cpp @@ -269,7 +269,7 @@ public: // Verify the consistency of the step. These checks are specific to // payments and assume that general checks were already performed. [[nodiscard]] TER - check(StrandContext const& ctx, std::shared_ptr const& sleSrc) const; + check(StrandContext const& ctx, SLE::const_ref sleSrc) const; [[nodiscard]] std::string logString() const override @@ -327,7 +327,7 @@ public: // Verify the consistency of the step. These checks are specific to // offer crossing and assume that general checks were already performed. static TER - check(StrandContext const& ctx, std::shared_ptr const& sleSrc); + check(StrandContext const& ctx, SLE::const_ref sleSrc); [[nodiscard]] std::string logString() const override @@ -415,7 +415,7 @@ DirectIOfferCrossingStep::maxFlow(ReadView const& sb, IOUAmount const& desired) } TER -DirectIPaymentStep::check(StrandContext const& ctx, std::shared_ptr const& sleSrc) const +DirectIPaymentStep::check(StrandContext const& ctx, SLE::const_ref sleSrc) const { // Since this is a payment a trust line must be present. Perform all // trust line related checks. @@ -463,7 +463,7 @@ DirectIPaymentStep::check(StrandContext const& ctx, std::shared_ptr c } TER -DirectIOfferCrossingStep::check(StrandContext const&, std::shared_ptr const&) +DirectIOfferCrossingStep::check(StrandContext const&, SLE::const_ref) { // The standard checks are all we can do because any remaining checks // require the existence of a trust line. Offer crossing does not diff --git a/src/libxrpl/tx/paths/MPTEndpointStep.cpp b/src/libxrpl/tx/paths/MPTEndpointStep.cpp index 5595f8ea65..7dcd6d9241 100644 --- a/src/libxrpl/tx/paths/MPTEndpointStep.cpp +++ b/src/libxrpl/tx/paths/MPTEndpointStep.cpp @@ -264,7 +264,7 @@ public: // Verify the consistency of the step. These checks are specific to // payments and assume that general checks were already performed. [[nodiscard]] TER - check(StrandContext const& ctx, std::shared_ptr const& sleSrc) const; + check(StrandContext const& ctx, SLE::const_ref sleSrc) const; [[nodiscard]] std::string logString() const override @@ -312,7 +312,7 @@ public: // Verify the consistency of the step. These checks are specific to // offer crossing and assume that general checks were already performed. static TER - check(StrandContext const& ctx, std::shared_ptr const& sleSrc); + check(StrandContext const& ctx, SLE::const_ref sleSrc); [[nodiscard]] std::string logString() const override @@ -328,8 +328,7 @@ public: //------------------------------------------------------------------------------ TER -MPTEndpointPaymentStep::check(StrandContext const& ctx, std::shared_ptr const& sleSrc) - const +MPTEndpointPaymentStep::check(StrandContext const& ctx, SLE::const_ref sleSrc) const { // Since this is a payment, MPToken must be present. Perform all // MPToken related checks. @@ -393,7 +392,7 @@ MPTEndpointPaymentStep::check(StrandContext const& ctx, std::shared_ptr const&) +MPTEndpointOfferCrossingStep::check(StrandContext const& ctx, SLE::const_ref) { // The standard checks are all we can do because any remaining checks // require the existence of a MPToken. Offer crossing does not diff --git a/src/libxrpl/tx/paths/OfferStream.cpp b/src/libxrpl/tx/paths/OfferStream.cpp index bfe2bbd1bb..b7defb4df8 100644 --- a/src/libxrpl/tx/paths/OfferStream.cpp +++ b/src/libxrpl/tx/paths/OfferStream.cpp @@ -27,7 +27,6 @@ #include #include -#include #include namespace xrpl { @@ -205,7 +204,7 @@ TOfferStreamBase::step() if (!tip_.step(j_)) return false; - std::shared_ptr const entry = tip_.entry(); + SLE::pointer const entry = tip_.entry(); // If we exceed the maximum number of allowed steps, we're done. if (!counter_.step()) diff --git a/src/libxrpl/tx/transactors/account/AccountDelete.cpp b/src/libxrpl/tx/transactors/account/AccountDelete.cpp index c0e8fe05c6..833f1c8b25 100644 --- a/src/libxrpl/tx/transactors/account/AccountDelete.cpp +++ b/src/libxrpl/tx/transactors/account/AccountDelete.cpp @@ -32,7 +32,6 @@ #include #include -#include #include namespace xrpl { @@ -72,7 +71,7 @@ using DeleterFuncPtr = TER (*)( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j); // Local function definitions that provides signature compatibility. @@ -82,7 +81,7 @@ offerDelete( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return offerDelete(view, sleDel, j); @@ -94,7 +93,7 @@ removeSignersFromLedger( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return SignerListSet::removeFromLedger(registry, view, account, j); @@ -106,7 +105,7 @@ removeTicketFromLedger( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const&, + SLE::ref, beast::Journal j) { return Transactor::ticketDelete(view, account, delIndex, j); @@ -118,7 +117,7 @@ removeDepositPreauthFromLedger( ApplyView& view, AccountID const&, uint256 const& delIndex, - std::shared_ptr const&, + SLE::ref, beast::Journal j) { return DepositPreauth::removeFromLedger(view, delIndex, j); @@ -130,7 +129,7 @@ removeNFTokenOfferFromLedger( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal) { if (!nft::deleteTokenOffer(view, sleDel)) @@ -145,7 +144,7 @@ removeDIDFromLedger( ApplyView& view, AccountID const& account, uint256 const& delIndex, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return DIDDelete::deleteSLE(view, sleDel, account, j); @@ -157,7 +156,7 @@ removeOracleFromLedger( ApplyView& view, AccountID const& account, uint256 const&, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return OracleDelete::deleteOracle(view, sleDel, account, j); @@ -169,7 +168,7 @@ removeCredentialFromLedger( ApplyView& view, AccountID const&, uint256 const&, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return credentials::deleteSLE(view, sleDel, j); @@ -181,7 +180,7 @@ removeDelegateFromLedger( ApplyView& view, AccountID const&, uint256 const&, - std::shared_ptr const& sleDel, + SLE::ref sleDel, beast::Journal j) { return DelegateSet::deleteDelegate(view, sleDel, j); @@ -301,7 +300,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx) if (dirIsEmpty(ctx.view, ownerDirKeylet)) return tesSUCCESS; - std::shared_ptr sleDirNode{}; + SLE::const_pointer sleDirNode{}; unsigned int uDirEntry{0}; uint256 dirEntry{beast::kZero}; @@ -368,7 +367,7 @@ AccountDelete::doApply() ownerDirKeylet, [&](LedgerEntryType nodeType, uint256 const& dirEntry, - std::shared_ptr& sleItem) -> std::pair { + SLE::pointer& sleItem) -> std::pair { if (auto deleter = nonObligationDeleter(nodeType)) { TER const result{deleter(ctx_.registry, view(), accountID_, dirEntry, sleItem, j_)}; @@ -417,10 +416,7 @@ AccountDelete::doApply() } void -AccountDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AccountDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/account/AccountSet.cpp b/src/libxrpl/tx/transactors/account/AccountSet.cpp index b52db14720..bc207b39dc 100644 --- a/src/libxrpl/tx/transactors/account/AccountSet.cpp +++ b/src/libxrpl/tx/transactors/account/AccountSet.cpp @@ -26,7 +26,6 @@ #include #include -#include #include namespace xrpl { @@ -643,10 +642,7 @@ AccountSet::doApply() } void -AccountSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AccountSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/account/SetRegularKey.cpp b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp index 66a0bef336..08ccf5e7ab 100644 --- a/src/libxrpl/tx/transactors/account/SetRegularKey.cpp +++ b/src/libxrpl/tx/transactors/account/SetRegularKey.cpp @@ -13,8 +13,6 @@ #include #include -#include - namespace xrpl { XRPAmount @@ -81,10 +79,7 @@ SetRegularKey::doApply() } void -SetRegularKey::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +SetRegularKey::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index 7a5d2c60b0..cedb9ace78 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -406,10 +406,7 @@ SignerListSet::writeSignersToSLE(SLE::pointer const& ledgerEntry, std::uint32_t } void -SignerListSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +SignerListSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 76b274a609..7c9c9d95dc 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -798,21 +798,21 @@ readOrpeekBridge(F&& getter, STXChainBridge const& bridgeSpec) return tryGet(STXChainBridge::ChainType::Issuing); } -std::shared_ptr +SLE::pointer peekBridge(ApplyView& v, STXChainBridge const& bridgeSpec) { return readOrpeekBridge( - [&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> std::shared_ptr { + [&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> SLE::pointer { return v.peek(keylet::bridge(b, ct)); }, bridgeSpec); } -std::shared_ptr +SLE::const_pointer readBridge(ReadView const& v, STXChainBridge const& bridgeSpec) { return readOrpeekBridge( - [&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> std::shared_ptr { + [&v](STXChainBridge const& b, STXChainBridge::ChainType ct) -> SLE::const_pointer { return v.read(keylet::bridge(b, ct)); }, bridgeSpec); @@ -2225,10 +2225,7 @@ XChainCreateAccountCommit::doApply() } void -XChainCreateBridge::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainCreateBridge::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2246,10 +2243,7 @@ XChainCreateBridge::finalizeInvariants( } void -BridgeModify::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +BridgeModify::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2267,10 +2261,7 @@ BridgeModify::finalizeInvariants( } void -XChainClaim::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainClaim::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2283,10 +2274,7 @@ XChainClaim::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, be } void -XChainCommit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainCommit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2304,10 +2292,7 @@ XChainCommit::finalizeInvariants( } void -XChainCreateClaimID::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainCreateClaimID::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2325,10 +2310,7 @@ XChainCreateClaimID::finalizeInvariants( } void -XChainAddClaimAttestation::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainAddClaimAttestation::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2346,10 +2328,7 @@ XChainAddClaimAttestation::finalizeInvariants( } void -XChainAddAccountCreateAttestation::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainAddAccountCreateAttestation::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } @@ -2367,10 +2346,7 @@ XChainAddAccountCreateAttestation::finalizeInvariants( } void -XChainCreateAccountCommit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +XChainCreateAccountCommit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/check/CheckCancel.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp index 7f1708fd81..d966772191 100644 --- a/src/libxrpl/tx/transactors/check/CheckCancel.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -14,8 +14,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -102,10 +100,7 @@ CheckCancel::doApply() } void -CheckCancel::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CheckCancel::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index af903ff177..c5813ae42d 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -31,7 +31,6 @@ #include #include -#include #include namespace xrpl { @@ -388,7 +387,7 @@ CheckCash::doApply() // Check reserve. Return destination account SLE if enough reserve, // otherwise return nullptr. - auto checkReserve = [&]() -> std::shared_ptr { + auto checkReserve = [&]() -> SLE::pointer { auto sleDst = psb.peek(keylet::account(accountID_)); // Can the account cover the trust line's or MPT reserve? @@ -592,10 +591,7 @@ CheckCash::doApply() } void -CheckCash::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CheckCash::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/check/CheckCreate.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp index d2d84536de..ff82912a1f 100644 --- a/src/libxrpl/tx/transactors/check/CheckCreate.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -258,10 +258,7 @@ CheckCreate::doApply() } void -CheckCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CheckCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index 4e9857d782..e0ebdd893a 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -20,8 +20,6 @@ #include #include -#include - namespace xrpl { using namespace credentials; @@ -126,10 +124,7 @@ CredentialAccept::doApply() } void -CredentialAccept::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CredentialAccept::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index bcb0a6fefa..acca408a95 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -179,10 +179,7 @@ CredentialCreate::doApply() } void -CredentialCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CredentialCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp b/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp index fcd1848cbe..6bd4ad54c5 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialDelete.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { using namespace credentials; @@ -97,10 +95,7 @@ CredentialDelete::doApply() } void -CredentialDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +CredentialDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 30d703686c..82fe88aa9f 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -132,7 +132,7 @@ DelegateSet::doApply() } TER -DelegateSet::deleteDelegate(ApplyView& view, std::shared_ptr const& sle, beast::Journal j) +DelegateSet::deleteDelegate(ApplyView& view, SLE::ref sle, beast::Journal j) { if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE @@ -174,10 +174,7 @@ DelegateSet::deleteDelegate(ApplyView& view, std::shared_ptr const& sle, be } void -DelegateSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +DelegateSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp index 4d3d97e443..dc6c98f95e 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateUtils.cpp @@ -7,12 +7,11 @@ #include #include -#include #include namespace xrpl { NotTEC -checkTxPermission(std::shared_ptr const& delegate, STTx const& tx) +checkTxPermission(SLE::const_ref delegate, STTx const& tx) { if (!delegate) return terNO_DELEGATE_PERMISSION; @@ -32,7 +31,7 @@ checkTxPermission(std::shared_ptr const& delegate, STTx const& tx) void loadGranularPermission( - std::shared_ptr const& delegate, + SLE::const_ref delegate, TxType const& txType, std::unordered_set& granularPermissions) { diff --git a/src/libxrpl/tx/transactors/dex/AMMBid.cpp b/src/libxrpl/tx/transactors/dex/AMMBid.cpp index b3b41fbfa2..a98f439d0a 100644 --- a/src/libxrpl/tx/transactors/dex/AMMBid.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMBid.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -379,10 +378,7 @@ AMMBid::doApply() } void -AMMBid::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMBid::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMClawback.cpp b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp index 43abc6ae29..b94e97e931 100644 --- a/src/libxrpl/tx/transactors/dex/AMMClawback.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMClawback.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include @@ -383,10 +382,7 @@ AMMClawback::equalWithdrawMatchingOneAmount( } void -AMMClawback::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMClawback::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index 60508eab85..9c1a5cfacb 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -389,10 +389,7 @@ AMMCreate::doApply() } void -AMMCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMDelete.cpp b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp index 8a8cab1c03..e2ecec8242 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDelete.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDelete.cpp @@ -15,8 +15,6 @@ #include #include -#include - namespace xrpl { bool @@ -67,10 +65,7 @@ AMMDelete::doApply() } void -AMMDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp index f45529f617..91858e3cd7 100644 --- a/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMDeposit.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -1014,10 +1013,7 @@ AMMDeposit::equalDepositInEmptyState( } void -AMMDeposit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMDeposit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMVote.cpp b/src/libxrpl/tx/transactors/dex/AMMVote.cpp index 391f7e1ecc..23604d46cc 100644 --- a/src/libxrpl/tx/transactors/dex/AMMVote.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMVote.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include @@ -249,10 +248,7 @@ AMMVote::doApply() } void -AMMVote::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMVote::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index 352f637f2f..e57f8558ff 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -758,7 +757,7 @@ AMMWithdraw::equalWithdrawTokens( std::pair AMMWithdraw::deleteAMMAccountIfEmpty( Sandbox& sb, - std::shared_ptr const ammSle, + SLE::pointer const ammSle, STAmount const& lpTokenBalance, Asset const& asset1, Asset const& asset2, @@ -1137,10 +1136,7 @@ AMMWithdraw::isWithdrawAll(STTx const& tx) return WithdrawAll::No; } void -AMMWithdraw::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +AMMWithdraw::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/OfferCancel.cpp b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp index 42692b59cc..0dea5fa967 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCancel.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCancel.cpp @@ -10,8 +10,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -70,10 +68,7 @@ OfferCancel::doApply() } void -OfferCancel::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +OfferCancel::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index f982837c5e..547d40e7b8 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -559,7 +559,7 @@ OfferCreate::formatAmount(STAmount const& amount) TER OfferCreate::applyHybrid( Sandbox& sb, - std::shared_ptr sleOffer, + STLedgerEntry::pointer sleOffer, Keylet const& offerKey, STAmount const& saTakerPays, STAmount const& saTakerGets, @@ -990,10 +990,7 @@ OfferCreate::doApply() } void -OfferCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +OfferCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp index 4b4fb47169..90aa21d8a1 100644 --- a/src/libxrpl/tx/transactors/did/DIDDelete.cpp +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -37,11 +35,7 @@ DIDDelete::deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner) } TER -DIDDelete::deleteSLE( - ApplyView& view, - std::shared_ptr sle, - AccountID const owner, - beast::Journal j) +DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, beast::Journal j) { // Remove object from owner directory if (!view.dirRemove(keylet::ownerDir(owner), (*sle)[sfOwnerNode], sle->key(), true)) @@ -71,10 +65,7 @@ DIDDelete::doApply() } void -DIDDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +DIDDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index a930c3c754..1392581bb0 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -63,7 +63,7 @@ DIDSet::preflight(PreflightContext const& ctx) } static TER -addSLE(ApplyContext& ctx, std::shared_ptr const& sle, AccountID const& owner) +addSLE(ApplyContext& ctx, SLE::ref sle, AccountID const& owner) { auto const sleAccount = ctx.view().peek(keylet::account(owner)); if (!sleAccount) @@ -150,10 +150,7 @@ DIDSet::doApply() } void -DIDSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +DIDSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp index 123f83a1a6..b8f4604d73 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -220,10 +219,7 @@ EscrowCancel::doApply() } void -EscrowCancel::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +EscrowCancel::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 4de302db3e..0a12e2d1bc 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -541,10 +541,7 @@ EscrowCreate::doApply() } void -EscrowCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +EscrowCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index 13bd4b1682..4cda867b48 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include @@ -401,10 +400,7 @@ EscrowFinish::doApply() } void -EscrowFinish::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +EscrowFinish::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp index 11095fdebe..0e1a4b3a3d 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -377,10 +376,7 @@ LoanBrokerCoverClawback::doApply() } void -LoanBrokerCoverClawback::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerCoverClawback::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp index e84f277f5b..537996ba57 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverDeposit.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { bool @@ -181,10 +179,7 @@ LoanBrokerCoverDeposit::doApply() } void -LoanBrokerCoverDeposit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerCoverDeposit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp index f4c95541f6..fea6f3b9cb 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverWithdraw.cpp @@ -20,8 +20,6 @@ #include #include -#include - namespace xrpl { bool @@ -204,10 +202,7 @@ LoanBrokerCoverWithdraw::doApply() } void -LoanBrokerCoverWithdraw::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerCoverWithdraw::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 6b77914370..f3c000bf0b 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -18,8 +18,6 @@ #include #include -#include - namespace xrpl { bool @@ -206,10 +204,7 @@ LoanBrokerDelete::doApply() } void -LoanBrokerDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index 6e3d2ad0a8..2dc003eb7f 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -279,10 +279,7 @@ LoanBrokerSet::doApply() } void -LoanBrokerSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanBrokerSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index 813700bd7a..d4ec92a9fb 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { bool @@ -142,10 +140,7 @@ LoanDelete::doApply() } void -LoanDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanManage.cpp b/src/libxrpl/tx/transactors/lending/LoanManage.cpp index d3165006b5..2b5c9d25f6 100644 --- a/src/libxrpl/tx/transactors/lending/LoanManage.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanManage.cpp @@ -26,8 +26,6 @@ #include #include -#include - namespace xrpl { bool @@ -435,10 +433,7 @@ LoanManage::doApply() } void -LoanManage::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanManage::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanPay.cpp b/src/libxrpl/tx/transactors/lending/LoanPay.cpp index 65220573de..a0a1479bdb 100644 --- a/src/libxrpl/tx/transactors/lending/LoanPay.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanPay.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include namespace xrpl { @@ -831,10 +830,7 @@ LoanPay::doApply() } void -LoanPay::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanPay::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index 561ab6b2aa..573f700f51 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -656,10 +656,7 @@ LoanSet::doApply() } void -LoanSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LoanSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index b80d282d70..6a82a15044 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -55,7 +54,7 @@ TER NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { auto const checkOffer = - [&ctx](std::optional id) -> std::pair, TER> { + [&ctx](std::optional id) -> std::pair { if (id) { if (id->isZero()) @@ -403,7 +402,7 @@ NFTokenAcceptOffer::transferNFToken( } TER -NFTokenAcceptOffer::acceptOffer(std::shared_ptr const& offer) +NFTokenAcceptOffer::acceptOffer(SLE::ref offer) { bool const isSell = offer->isFlag(lsfSellNFToken); AccountID const owner = (*offer)[sfOwner]; @@ -441,7 +440,7 @@ TER NFTokenAcceptOffer::doApply() { auto const loadToken = [this](std::optional const& id) { - std::shared_ptr sle; + SLE::pointer sle; if (id) sle = view().peek(keylet::nftoffer(*id)); return sle; @@ -456,8 +455,7 @@ NFTokenAcceptOffer::doApply() { bool foundExpired = false; - auto const deleteOfferIfExpired = - [this, &foundExpired](std::shared_ptr const& offer) -> TER { + auto const deleteOfferIfExpired = [this, &foundExpired](SLE::ref offer) -> TER { if (offer && hasExpired(view(), (*offer)[~sfExpiration])) { JLOG(j_.trace()) << "Offer is expired, deleting: " << offer->key(); @@ -569,10 +567,7 @@ NFTokenAcceptOffer::doApply() } void -NFTokenAcceptOffer::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenAcceptOffer::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp index 871a8e706a..d33b1af8c2 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenBurn.cpp @@ -13,8 +13,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -95,10 +93,7 @@ NFTokenBurn::doApply() } void -NFTokenBurn::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenBurn::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp index 924dc49269..1614f90202 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp @@ -16,8 +16,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -98,10 +96,7 @@ NFTokenCancelOffer::doApply() } void -NFTokenCancelOffer::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenCancelOffer::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp index 9c7fe7d5ef..1948f3803d 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCreateOffer.cpp @@ -14,8 +14,6 @@ #include #include -#include - namespace xrpl { std::uint32_t @@ -91,10 +89,7 @@ NFTokenCreateOffer::doApply() } void -NFTokenCreateOffer::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenCreateOffer::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index e7faa30df8..bd7413d50b 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -27,7 +27,6 @@ #include #include #include // IWYU pragma: keep -#include #include namespace xrpl { @@ -344,10 +343,7 @@ NFTokenMint::doApply() } void -NFTokenMint::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenMint::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp index 7e3eeeefee..32ba362ded 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenModify.cpp @@ -14,8 +14,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -69,10 +67,7 @@ NFTokenModify::doApply() } void -NFTokenModify::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +NFTokenModify::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index 839b2b4e24..9a26816155 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -13,8 +13,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -51,7 +49,7 @@ OracleDelete::preclaim(PreclaimContext const& ctx) TER OracleDelete::deleteOracle( ApplyView& view, - std::shared_ptr const& sle, + SLE::ref sle, AccountID const& account, beast::Journal j) { @@ -89,10 +87,7 @@ OracleDelete::doApply() } void -OracleDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +OracleDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index 66ce95f46c..12d826e54d 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -328,10 +328,7 @@ OracleSet::doApply() } void -OracleSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +OracleSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 8b4ad2f025..7e950dc743 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -298,10 +298,7 @@ DepositPreauth::removeFromLedger(ApplyView& view, uint256 const& preauthIndex, b } void -DepositPreauth::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +DepositPreauth::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment/Payment.cpp b/src/libxrpl/tx/transactors/payment/Payment.cpp index d7ad5d3b3c..805ebe3684 100644 --- a/src/libxrpl/tx/transactors/payment/Payment.cpp +++ b/src/libxrpl/tx/transactors/payment/Payment.cpp @@ -679,10 +679,7 @@ Payment::doApply() } void -Payment::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +Payment::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp index cc99b8f62d..b1fe5e24bc 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -200,10 +199,7 @@ PaymentChannelClaim::doApply() } void -PaymentChannelClaim::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PaymentChannelClaim::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp index 7ce25b6d15..63dbe01944 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -185,10 +185,7 @@ PaymentChannelCreate::doApply() } void -PaymentChannelCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PaymentChannelCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp index 41906aa3da..bcb8a91c96 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -18,8 +18,6 @@ #include #include -#include - namespace xrpl { TxConsequences @@ -107,10 +105,7 @@ PaymentChannelFund::doApply() } void -PaymentChannelFund::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PaymentChannelFund::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index e36a0eb584..7eb3f282b9 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -12,8 +12,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -74,10 +72,7 @@ PermissionedDomainDelete::doApply() } void -PermissionedDomainDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PermissionedDomainDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index ff4019ec59..0e71ceada1 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -133,10 +133,7 @@ PermissionedDomainSet::doApply() } void -PermissionedDomainSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +PermissionedDomainSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/system/Batch.cpp b/src/libxrpl/tx/transactors/system/Batch.cpp index b9440e3273..64a62ac273 100644 --- a/src/libxrpl/tx/transactors/system/Batch.cpp +++ b/src/libxrpl/tx/transactors/system/Batch.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -518,10 +517,7 @@ Batch::doApply() } void -Batch::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +Batch::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/system/Change.cpp b/src/libxrpl/tx/transactors/system/Change.cpp index 92a06fd807..38fee5bf92 100644 --- a/src/libxrpl/tx/transactors/system/Change.cpp +++ b/src/libxrpl/tx/transactors/system/Change.cpp @@ -409,10 +409,7 @@ Change::applyUNLModify() } void -Change::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +Change::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp index 222e5dce7a..65c32b788c 100644 --- a/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp +++ b/src/libxrpl/tx/transactors/system/LedgerStateFix.cpp @@ -16,7 +16,6 @@ #include #include -#include #include namespace xrpl { @@ -153,10 +152,7 @@ LedgerStateFix::doApply() } void -LedgerStateFix::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +LedgerStateFix::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp index f442ac223b..5be00fe76c 100644 --- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -135,10 +135,7 @@ TicketCreate::doApply() } void -TicketCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +TicketCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/Clawback.cpp b/src/libxrpl/tx/transactors/token/Clawback.cpp index 8532f97995..06132c1c97 100644 --- a/src/libxrpl/tx/transactors/token/Clawback.cpp +++ b/src/libxrpl/tx/transactors/token/Clawback.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -275,10 +274,7 @@ Clawback::doApply() } void -Clawback::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +Clawback::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp index 9b0c8d2b56..332d160cac 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenAuthorize.cpp @@ -15,8 +15,6 @@ #include #include -#include - namespace xrpl { std::uint32_t @@ -48,7 +46,7 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) // `holderID` is NOT used if (!holderID) { - std::shared_ptr const sleMpt = + SLE::const_pointer const sleMpt = ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], accountID)); // There is an edge case where all holders have zero balance, issuance @@ -156,10 +154,7 @@ MPTokenAuthorize::doApply() } void -MPTokenAuthorize::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +MPTokenAuthorize::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 64d0b01f5e..94a5ca848d 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -195,10 +195,7 @@ MPTokenIssuanceCreate::doApply() } void -MPTokenIssuanceCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +MPTokenIssuanceCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index 46811508b7..1029c25813 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -9,8 +9,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -59,10 +57,7 @@ MPTokenIssuanceDestroy::doApply() } void -MPTokenIssuanceDestroy::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +MPTokenIssuanceDestroy::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp index d8b62acf3f..9b25531161 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include namespace xrpl { @@ -270,7 +269,7 @@ MPTokenIssuanceSet::doApply() auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID]; auto const holderID = ctx_.tx[~sfHolder]; auto const domainID = ctx_.tx[~sfDomainID]; - std::shared_ptr sle; + SLE::pointer sle; if (holderID) { @@ -373,10 +372,7 @@ MPTokenIssuanceSet::doApply() } void -MPTokenIssuanceSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +MPTokenIssuanceSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index a8ea786347..1d2bc96693 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -27,7 +27,6 @@ #include #include -#include #include namespace { @@ -669,10 +668,7 @@ TrustSet::doApply() } void -TrustSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +TrustSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp index b5b2c1f384..eb12905467 100644 --- a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -61,7 +60,7 @@ VaultClawback::preflight(PreflightContext const& ctx) [[nodiscard]] STAmount clawbackAmount( - std::shared_ptr const& vault, + SLE::const_ref vault, std::optional const& maybeAmount, AccountID const& account) { @@ -221,8 +220,8 @@ VaultClawback::preclaim(PreclaimContext const& ctx) Expected, TER> VaultClawback::assetsToClawback( - std::shared_ptr const& vault, - std::shared_ptr const& sleShareIssuance, + SLE::ref vault, + SLE::const_ref sleShareIssuance, AccountID const& holder, STAmount const& clawbackAmount) { @@ -452,10 +451,7 @@ VaultClawback::doApply() } void -VaultClawback::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultClawback::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index 7490f44619..4163753014 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -263,10 +263,7 @@ VaultCreate::doApply() } void -VaultCreate::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultCreate::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index 438896e7ed..030a7e971c 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -17,8 +17,6 @@ #include #include -#include - namespace xrpl { NotTEC @@ -213,10 +211,7 @@ VaultDelete::doApply() } void -VaultDelete::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultDelete::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index 50d165a2ba..c08d1e957c 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -348,10 +347,7 @@ VaultDeposit::doApply() } void -VaultDeposit::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultDeposit::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultSet.cpp b/src/libxrpl/tx/transactors/vault/VaultSet.cpp index f384fd3fb1..6d0ade6e52 100644 --- a/src/libxrpl/tx/transactors/vault/VaultSet.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultSet.cpp @@ -15,8 +15,6 @@ #include #include -#include - namespace xrpl { bool @@ -180,10 +178,7 @@ VaultSet::doApply() } void -VaultSet::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index c52d94ac0b..cfcf79fdba 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace xrpl { @@ -368,10 +367,7 @@ VaultWithdraw::doApply() } void -VaultWithdraw::visitInvariantEntry( - bool, - std::shared_ptr const&, - std::shared_ptr const&) +VaultWithdraw::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } diff --git a/src/test/app/CheckMPT_test.cpp b/src/test/app/CheckMPT_test.cpp index 1ca24051dd..861a115fc9 100644 --- a/src/test/app/CheckMPT_test.cpp +++ b/src/test/app/CheckMPT_test.cpp @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -54,11 +53,11 @@ namespace xrpl { class CheckMPT_test : public beast::unit_test::Suite { // Helper function that returns the Checks on an account. - static std::vector> + static std::vector checksOnAccount(test::jtx::Env& env, test::jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle && sle->getType() == ltCHECK) result.push_back(sle); }); diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index 1d5861136c..9b814a24b1 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -43,7 +43,6 @@ #include #include -#include #include #include @@ -58,11 +57,11 @@ class Check_test : public beast::unit_test::Suite } // Helper function that returns the Checks on an account. - static std::vector> + static std::vector checksOnAccount(test::jtx::Env& env, test::jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle && sle->getType() == ltCHECK) result.push_back(sle); }); diff --git a/src/test/app/FlowMPT_test.cpp b/src/test/app/FlowMPT_test.cpp index e1d6f512d2..2e224a7eb1 100644 --- a/src/test/app/FlowMPT_test.cpp +++ b/src/test/app/FlowMPT_test.cpp @@ -37,7 +37,6 @@ #include #include -#include #include #include #include @@ -433,7 +432,7 @@ struct FlowMPT_test : public beast::unit_test::Suite env.require(Balance(bob, eur(999))); // Show that bob's USD offer is now a blocker. - std::shared_ptr const usdOffer = env.le(bobUsdOffer); + SLE::const_pointer const usdOffer = env.le(bobUsdOffer); if (BEAST_EXPECT(usdOffer)) { std::uint64_t const bookRate = [&usdOffer]() { @@ -730,11 +729,11 @@ struct FlowMPT_test : public beast::unit_test::Suite } // Helper function that returns the Offers on an account. - static std::vector> + static std::vector offersOnAccount(jtx::Env& env, jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle->getType() == ltOFFER) result.push_back(sle); }); @@ -1792,7 +1791,7 @@ struct FlowMPT_test : public beast::unit_test::Suite // but OutstandingAmount is 300USD because gw's sell offer is balanced out by // gw's buy offer. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 400, 400, 400, 400, 300, 100, 100, 100, 1100, 0, false}, + { .maxAmt=400, .sendMax=400, .dstTrustLimit=400, .dstExpectEUR=400, .outstandingUSD=300, .expEdBuyUSD=100, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: alice, carol, bob, gw are consumed. // Buy USD: john, gw, dan, ed (partially) are consumed. // gw's sell USD is partially consumed because there is available balance (50USD). @@ -1801,32 +1800,32 @@ struct FlowMPT_test : public beast::unit_test::Suite // gw's offer is removed from the order book because it's partially consumed and // the remaining offer is unfunded. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 350, 400, 400, 350, 250, 50, 100, 100, 1050, 0, false}, + { .maxAmt=350, .sendMax=400, .dstTrustLimit=400, .dstExpectEUR=350, .outstandingUSD=250, .expEdBuyUSD=50, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: alice, carol, bob are consumed; gw's is unfunded // since OutstandingAmount is initially at MaximumAmount. // Buy USD: john, gw, dan are consumed; ed's remains on the order // book since 300USD is the sell limit. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 400, 400, 300, 200, 0, 100, 100, 1000, 0, false}, + { .maxAmt=300, .sendMax=400, .dstTrustLimit=400, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. bill's trustline limit sets the output to 300USD. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 400, 300, 300, 200, 0, 100, 100, 1000, 0, false}, + { .maxAmt=300, .sendMax=400, .dstTrustLimit=300, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: alice, carol, bob are consumed; gw's removed from // the order book since it's unfunded. // Buy USD: john, gw, dan are consumed; ed's remains on the order // book since 300USD is the limit. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 400, 300, 300, 200, 0, 100, 100, 1000, 0, true}, + { .maxAmt=300, .sendMax=400, .dstTrustLimit=300, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: alice, carol are consumed; gw's removed from // the order book in rev pass since it's unfunded; bob's // remains on the order book. // Buy USD: john, gw; ed's, dan's remains on the order // book since 300USD is the limit. //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 200, 300, 200, 200, 0, 0, 0, 1000, 0, false}, + { .maxAmt=300, .sendMax=200, .dstTrustLimit=300, .dstExpectEUR=200, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=0, .expBobSellUSD=0, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as three tests above since limited by buy 300USD (gw offer is unfunded) //*maxAmt sendMax limitEUR expectEUR outstandingUSD edBuy danBuy bobSell gwXRP offersGw lastGw - { 300, 380, 400, 300, 200, 0, 100, 100, 1000, 0, false}, + { .maxAmt=300, .sendMax=380, .dstTrustLimit=400, .dstExpectEUR=300, .outstandingUSD=200, .expEdBuyUSD=0, .expDanBuyUSD=100, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=false}, }; // clang-format on for (auto const& t : tests) @@ -1912,26 +1911,26 @@ struct FlowMPT_test : public beast::unit_test::Suite // Gw gets 300USD from alice; carol and bob buy 200USD, // therefore OutstandingAmount is 200. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 100, 1300, 200, 100, 900, 0, false}, + { .maxAmt=300, .sendMax=300, .gwOffer=100, .dstExpectXRP=1300, .outstandingUSD=200, .expBobBuyUSD=100, .expGwXRP=900, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. Gw offer location in the order book doesn't matter //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 100, 1300, 200, 100, 900, 0, true}, + { .maxAmt=300, .sendMax=300, .gwOffer=100, .dstExpectXRP=1300, .outstandingUSD=200, .expBobBuyUSD=100, .expGwXRP=900, .expOffersGw=0, .lastGwBuyUSD=true}, // Buy USD: carol, gw are consumed. bob's offer remains on the order book. // Gw gets 300USD from alice; carol buys 100USD, // therefore OutstandingAmount is 100. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 200, 1300, 100, 0, 800, 0, false}, + { .maxAmt=300, .sendMax=300, .gwOffer=200, .dstExpectXRP=1300, .outstandingUSD=100, .expBobBuyUSD=0, .expGwXRP=800, .expOffersGw=0, .lastGwBuyUSD=false}, // Buy USD: carol, bob are consumed; gw's is partially consumed (100/100) since it's last. // Gw gets 300USD from alice; carol and bob buy 200USD, // therefore OutstandingAmount is 200. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 300, 200, 1300, 200, 100, 900, 1, true}, + { .maxAmt=300, .sendMax=300, .gwOffer=200, .dstExpectXRP=1300, .outstandingUSD=200, .expBobBuyUSD=100, .expGwXRP=900, .expOffersGw=1, .lastGwBuyUSD=true}, // Buy USD: carol, bob are consumed; gw's is partially consumed (50/50) since it's last // and sendMax limits the output. // Gw gets 250USD from alice; carol and bob buy 200USD, alice has 50USD left, // therefore OutstandingAmount is 200. //*maxAmt sendMax gwOffer dstXRP outstandingUSD bobBuy gwXRP offersGw lastGw - { 300, 250, 200, 1250, 250, 100, 950, 1, true}, + { .maxAmt=300, .sendMax=250, .gwOffer=200, .dstExpectXRP=1250, .outstandingUSD=250, .expBobBuyUSD=100, .expGwXRP=950, .expOffersGw=1, .lastGwBuyUSD=true}, }; // clang-format on for (auto const& t : tests) @@ -2024,10 +2023,10 @@ struct FlowMPT_test : public beast::unit_test::Suite // Sell USD: carol, gw, bob are consumed. // ed buys 300USD from carol, gw, bob therefore OutstandingAmount is 300. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 0, 100, 300, 300, 700, 100, 1100, 0, false}, + { .maxAmt=300, .sendMax=300, .initDst=0, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=700, .expBobSellUSD=100, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. Gw offer location in the order book doesn't matter //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 0, 100, 300, 300, 700, 100, 1100, 0, true}, + { .maxAmt=300, .sendMax=300, .initDst=0, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=700, .expBobSellUSD=100, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: carol, bob are consumed, gw is partially consumed. // ed buys 200 from carol and bob and 50 from gw because gw can only issue 50 // (300(max) - 200(carol+bob) - 50(ed)). ed buys 250 from carol, gw, bob and has 50 initially, @@ -2035,33 +2034,33 @@ struct FlowMPT_test : public beast::unit_test::Suite // gw's offer is removed from the order book because it's partially consumed and the remaining // offer is unfunded. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 50, 100, 300, 300, 750, 100, 1050, 0, false}, + { .maxAmt=300, .sendMax=300, .initDst=50, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=false}, // Same as above. Gw offer location in the order book doesn't matter. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 50, 100, 300, 300, 750, 100, 1050, 0, true}, + { .maxAmt=300, .sendMax=300, .initDst=50, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=true}, // Same as above. Gw offer size doesn't matter. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 300, 50, 200, 300, 300, 750, 100, 1050, 0, true}, + { .maxAmt=300, .sendMax=300, .initDst=50, .gwOffer=200, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: carol, gw are consumed, bob is partially consumed. // ed buys 200 from carol and gw and 50 form bob because of sendMax limit. bob keeps 50, // therefore OutstandingAmount is 300. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 0, 100, 250, 300, 750, 50, 1100, 0, false}, + { .maxAmt=300, .sendMax=250, .initDst=0, .gwOffer=100, .dstExpectUSD=250, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=50, .expGwXRP=1100, .expOffersGw=0, .lastGwBuyUSD=false}, // Sell USD: carol, bob are consumed, gw is partially consumed because of sendMax limit. // ed buys 200 from carol and bob and 50 from gw. Therefore, OutstandingAmount is 250. // gw's offer remains on the order book because it's partially consumed and has more funds. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 0, 100, 250, 250, 750, 100, 1050, 1, true}, + { .maxAmt=300, .sendMax=250, .initDst=0, .gwOffer=100, .dstExpectUSD=250, .outstandingUSD=250, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=1, .lastGwBuyUSD=true}, // Sell USD: carol, bob are consumed, gw is partially consumed because of sendMax limit, also // there is only 50 available to issue. ed buys 200 from carol and bob and 50 from gw, plus // he has initially 50, therefore OutstandingAmount is 300. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 50, 100, 300, 300, 750, 100, 1050, 0, true}, + { .maxAmt=300, .sendMax=250, .initDst=50, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=750, .expBobSellUSD=100, .expGwXRP=1050, .expOffersGw=0, .lastGwBuyUSD=true}, // Sell USD: carol, bob are consumed, gw is not consumed because there is not available funds // to issue. ed buys 200 from carol and bob and, plus he has initially 100, // therefore OutstandingAmount is 300. gw offer is removed because it's unfunded. //*maxAmt sendMax initDst gwOffer dstUSD outstandingUSD aliceXRP bobSell gwXRP offersGw lastGw - { 300, 250, 100, 100, 300, 300, 800, 100, 1000, 0, true}, + { .maxAmt=300, .sendMax=250, .initDst=100, .gwOffer=100, .dstExpectUSD=300, .outstandingUSD=300, .expAliceXRP=800, .expBobSellUSD=100, .expGwXRP=1000, .expOffersGw=0, .lastGwBuyUSD=true}, }; // clang-format on for (auto const& t : tests) diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index c6f4cf59d1..5f56a0ceb1 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -43,7 +43,6 @@ #include #include -#include #include #include #include @@ -572,7 +571,7 @@ struct Flow_test : public beast::unit_test::Suite env.require(Balance(bob, eur(999))); // Show that bob's USD offer is now a blocker. - std::shared_ptr const usdOffer = env.le(bobUsdOffer); + SLE::const_pointer const usdOffer = env.le(bobUsdOffer); if (BEAST_EXPECT(usdOffer)) { std::uint64_t const bookRate = [&usdOffer]() { @@ -711,11 +710,11 @@ struct Flow_test : public beast::unit_test::Suite } // Helper function that returns the Offers on an account. - static std::vector> + static std::vector offersOnAccount(jtx::Env& env, jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle->getType() == ltOFFER) result.push_back(sle); }); diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 432dccce61..3654036869 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -1277,7 +1277,7 @@ class Invariants_test : public beast::unit_test::Suite }); } - static std::shared_ptr + static SLE::pointer createPermissionedDomain( ApplyContext& ac, test::jtx::Account const& a1, diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index e9366f7c32..e0f2f4eab0 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -721,11 +720,11 @@ public: } // Helper function that returns the Offers on an account. - static std::vector> + static std::vector offersOnAccount(jtx::Env& env, jtx::Account account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle->getType() == ltOFFER) result.push_back(sle); }); @@ -3731,9 +3730,7 @@ public: auto const offerCount = std::distance( actorOffers.begin(), std::remove_if( - actorOffers.begin(), - actorOffers.end(), - [](std::shared_ptr& offer) { + actorOffers.begin(), actorOffers.end(), [](SLE::const_pointer& offer) { return (*offer)[sfTakerGets].signum() == 0; })); BEAST_EXPECT(offerCount == actor.offers); @@ -3903,9 +3900,7 @@ public: auto const offerCount = std::distance( actorOffers.begin(), std::remove_if( - actorOffers.begin(), - actorOffers.end(), - [](std::shared_ptr& offer) { + actorOffers.begin(), actorOffers.end(), [](SLE::const_pointer& offer) { return (*offer)[sfTakerGets].signum() == 0; })); BEAST_EXPECT(offerCount == actor.offers); @@ -4239,15 +4234,13 @@ public: } // Helper function that returns offers on an account sorted by sequence. - static std::vector> + static std::vector sortedOffersOnAccount(jtx::Env& env, jtx::Account const& acct) { - std::vector> offers{offersOnAccount(env, acct)}; - std::ranges::sort( - offers, - [](std::shared_ptr const& rhs, std::shared_ptr const& lhs) { - return (*rhs)[sfSequence] < (*lhs)[sfSequence]; - }); + std::vector offers{offersOnAccount(env, acct)}; + std::ranges::sort(offers, [](SLE::const_ref rhs, SLE::const_ref lhs) { + return (*rhs)[sfSequence] < (*lhs)[sfSequence]; + }); return offers; } @@ -4692,14 +4685,14 @@ public: // IOU/IOU, XRP/IOU, IOU/XRP offers have TickSize logic unchanged // IOU/MPT, MPT/IOU have TickSize logic applied to adjust IOU only std::vector const tests = { - {getIOU, getIOU, 10, 30}, - {getIOU, getXRP, 10, 30'000'000}, - {getXRP, getIOU, 10'000'000, 30}, - {getMPT, getXRP, 10'000'000, 30'000'000}, - {getXRP, getMPT, 10'000'000, 30'000'000}, - {getIOU, getMPT, 10, 30'000'000}, - {getMPT, getIOU, 10'000'000, 30}, - {getMPT, getMPT, 10'000'000, 30'000'000}}; + {.toAsset1 = getIOU, .toAsset2 = getIOU, .val1 = 10, .val2 = 30}, + {.toAsset1 = getIOU, .toAsset2 = getXRP, .val1 = 10, .val2 = 30'000'000}, + {.toAsset1 = getXRP, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30}, + {.toAsset1 = getMPT, .toAsset2 = getXRP, .val1 = 10'000'000, .val2 = 30'000'000}, + {.toAsset1 = getXRP, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000}, + {.toAsset1 = getIOU, .toAsset2 = getMPT, .val1 = 10, .val2 = 30'000'000}, + {.toAsset1 = getMPT, .toAsset2 = getIOU, .val1 = 10'000'000, .val2 = 30}, + {.toAsset1 = getMPT, .toAsset2 = getMPT, .val1 = 10'000'000, .val2 = 30'000'000}}; for (TestInfo const& t : tests) { Env env{*this, features}; @@ -4731,7 +4724,7 @@ public: env(offer(alice, xts(t.val2), xxx(t.val1)), Json(jss::Flags, tfSell)); std::map> offers; - forEachItem(*env.current(), alice, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), alice, [&](SLE::const_ref sle) { if (sle->getType() == ltOFFER) { offers.emplace( diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index 811a18dda5..83c58884e0 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -755,11 +754,11 @@ public: } // Helper function that returns the Offers on an account. - static std::vector> + static std::vector offersOnAccount(jtx::Env& env, jtx::Account const& account) { - std::vector> result; - forEachItem(*env.current(), account, [&result](std::shared_ptr const& sle) { + std::vector result; + forEachItem(*env.current(), account, [&result](SLE::const_ref sle) { if (sle->getType() == ltOFFER) result.push_back(sle); }); @@ -3928,10 +3927,10 @@ public: // clang-format off TestData const tests[]{ // btcStart --------------------- actor[0] --------------------- -------------------- actor[1] ------------------- - {.self=0, .leg0=0, .leg1=1, .btcStart=btc(20), .actors={{"ann", 0, drops(3900000'000000 - (4 * baseFee)), btc(20.0), usd(3000)}, {"abe", 0, drops(4100000'000000 - (3 * baseFee)), btc( 0), usd(750)}}}, // no BTC xfer fee - {.self=0, .leg0=1, .leg1=0, .btcStart=btc(20), .actors={{"bev", 0, drops(4100000'000000 - (4 * baseFee)), btc( 7.5), usd(2000)}, {"bob", 0, drops(3900000'000000 - (3 * baseFee)), btc(10), usd( 0)}}}, // no USD xfer fee - {.self=0, .leg0=0, .leg1=0, .btcStart=btc(20), .actors={{"cam", 0, drops(4000000'000000 - (5 * baseFee)), btc(20.0), usd(2000)} }}, // no xfer fee - {.self=0, .leg0=1, .leg1=0, .btcStart=btc( 5), .actors={{"deb", 1, drops(4040000'000000 - (4 * baseFee)), btc( 0.0), usd(2000)}, {"dan", 1, drops(3960000'000000 - (3 * baseFee)), btc( 4), usd( 0)}}}, // no USD xfer fee + {.self=0, .leg0=0, .leg1=1, .btcStart=btc(20), .actors={{.acct="ann", .offers=0, .xrp=drops(3900000'000000 - (4 * baseFee)), .btc=btc(20.0), .usd=usd(3000)}, {.acct="abe", .offers=0, .xrp=drops(4100000'000000 - (3 * baseFee)), .btc=btc( 0), .usd=usd(750)}}}, // no BTC xfer fee + {.self=0, .leg0=1, .leg1=0, .btcStart=btc(20), .actors={{.acct="bev", .offers=0, .xrp=drops(4100000'000000 - (4 * baseFee)), .btc=btc( 7.5), .usd=usd(2000)}, {.acct="bob", .offers=0, .xrp=drops(3900000'000000 - (3 * baseFee)), .btc=btc(10), .usd=usd( 0)}}}, // no USD xfer fee + {.self=0, .leg0=0, .leg1=0, .btcStart=btc(20), .actors={{.acct="cam", .offers=0, .xrp=drops(4000000'000000 - (5 * baseFee)), .btc=btc(20.0), .usd=usd(2000)} }}, // no xfer fee + {.self=0, .leg0=1, .leg1=0, .btcStart=btc( 5), .actors={{.acct="deb", .offers=1, .xrp=drops(4040000'000000 - (4 * baseFee)), .btc=btc( 0.0), .usd=usd(2000)}, {.acct="dan", .offers=1, .xrp=drops(3960000'000000 - (3 * baseFee)), .btc=btc( 4), .usd=usd( 0)}}}, // no USD xfer fee }; // clang-format on @@ -3980,7 +3979,7 @@ public: auto actorOffers = offersOnAccount(env, actor.acct); auto const offerCount = std::distance( actorOffers.begin(), - std::ranges::remove_if(actorOffers, [](std::shared_ptr& offer) { + std::ranges::remove_if(actorOffers, [](SLE::const_pointer& offer) { return (*offer)[sfTakerGets].signum() == 0; }).begin()); BEAST_EXPECT(offerCount == actor.offers); @@ -4076,8 +4075,8 @@ public: // clang-format off TestData const tests[]{ // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] -------------------- - {.self=0, .leg0=0, .leg1=1, .btcStart=btc(5), .actors={{"gay", 1, drops(3950000'000000 - (4 * baseFee)), btc(5), usd(2500)}, {"gar", 1, drops(4050000'000000 - (3 * baseFee)), btc(0), usd(1375)}}}, // no BTC xfer fee - {.self=0, .leg0=0, .leg1=0, .btcStart=btc(5), .actors={{"hye", 2, drops(4000000'000000 - (5 * baseFee)), btc(5), usd(2000)} }} // no xfer fee + {.self=0, .leg0=0, .leg1=1, .btcStart=btc(5), .actors={{.acct="gay", .offers=1, .xrp=drops(3950000'000000 - (4 * baseFee)), .btc=btc(5), .usd=usd(2500)}, {.acct="gar", .offers=1, .xrp=drops(4050000'000000 - (3 * baseFee)), .btc=btc(0), .usd=usd(1375)}}}, // no BTC xfer fee + {.self=0, .leg0=0, .leg1=0, .btcStart=btc(5), .actors={{.acct="hye", .offers=2, .xrp=drops(4000000'000000 - (5 * baseFee)), .btc=btc(5), .usd=usd(2000)} }} // no xfer fee }; // clang-format on @@ -4126,7 +4125,7 @@ public: auto actorOffers = offersOnAccount(env, actor.acct); auto const offerCount = std::distance( actorOffers.begin(), - std::ranges::remove_if(actorOffers, [](std::shared_ptr& offer) { + std::ranges::remove_if(actorOffers, [](SLE::const_pointer& offer) { return (*offer)[sfTakerGets].signum() == 0; }).begin()); BEAST_EXPECT(offerCount == actor.offers); @@ -4641,7 +4640,7 @@ public: env(offer(alice, xts(30), xxx(10)), Json(jss::Flags, tfSell)); std::map> offers; - forEachItem(*env.current(), alice, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), alice, [&](SLE::const_ref sle) { if (sle->getType() == ltOFFER) { offers.emplace( @@ -4676,15 +4675,13 @@ public: } // Helper function that returns offers on an account sorted by sequence. - static std::vector> + static std::vector sortedOffersOnAccount(jtx::Env& env, jtx::Account const& acct) { - std::vector> offers{offersOnAccount(env, acct)}; - std::ranges::sort( - offers, - [](std::shared_ptr const& rhs, std::shared_ptr const& lhs) { - return (*rhs)[sfSequence] < (*lhs)[sfSequence]; - }); + std::vector offers{offersOnAccount(env, acct)}; + std::ranges::sort(offers, [](SLE::const_ref rhs, SLE::const_ref lhs) { + return (*rhs)[sfSequence] < (*lhs)[sfSequence]; + }); return offers; } diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index b81afa830e..0b4222ca48 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -57,7 +56,7 @@ using namespace jtx::paychan; struct PayChan_test : public beast::unit_test::Suite { - static std::pair> + static std::pair channelKeyAndSle(ReadView const& view, jtx::Account const& account, jtx::Account const& dst) { auto const sle = view.read(keylet::account(account)); @@ -869,7 +868,7 @@ struct PayChan_test : public beast::unit_test::Suite env.close(); // Setup deposit authorization - env(deposit::authCredentials(bob, {{carol, credType}})); + env(deposit::authCredentials(bob, {{.issuer = carol, .credType = credType}})); env.close(); // Fail, credentials doesn’t belong to root account @@ -1665,9 +1664,8 @@ struct PayChan_test : public beast::unit_test::Suite auto const settleDelay = 100s; auto const pk = alice.pk(); - auto inOwnerDir = [](ReadView const& view, - Account const& acc, - std::shared_ptr const& chan) -> bool { + auto inOwnerDir = + [](ReadView const& view, Account const& acc, SLE::const_ref chan) -> bool { xrpl::Dir const ownerDir(view, keylet::ownerDir(acc.id())); // NOLINTNEXTLINE(modernize-use-ranges) return std::find(ownerDir.begin(), ownerDir.end(), chan) != ownerDir.end(); diff --git a/src/test/app/XChain_test.cpp b/src/test/app/XChain_test.cpp index 0198a36e96..de80444f2e 100644 --- a/src/test/app/XChain_test.cpp +++ b/src/test/app/XChain_test.cpp @@ -150,18 +150,18 @@ struct SEnv return env.current()->fees().base; } - std::shared_ptr + SLE::const_pointer account(jtx::Account const& account) { return env.le(account); } - std::shared_ptr + SLE::const_pointer bridge(json::Value const& jvb) { STXChainBridge const b(jvb); - auto tryGet = [&](STXChainBridge::ChainType ct) -> std::shared_ptr { + auto tryGet = [&](STXChainBridge::ChainType ct) -> SLE::const_pointer { if (auto r = env.le(keylet::bridge(b, ct))) { if ((*r)[sfXChainBridge] == b) @@ -186,13 +186,13 @@ struct SEnv return (*bridge(jvb))[sfXChainClaimID]; } - std::shared_ptr + SLE::const_pointer claimID(json::Value const& jvb, std::uint64_t seq) { return env.le(keylet::xChainClaimID(STXChainBridge(jvb), seq)); } - std::shared_ptr + SLE::const_pointer caClaimID(json::Value const& jvb, std::uint64_t seq) { return env.le(keylet::xChainCreateAccountClaimID(STXChainBridge(jvb), seq)); diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index 4618b36fde..3d813d993c 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -531,13 +531,13 @@ public: /** Return an account root. @return empty if the account does not exist. */ - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer le(Account const& account) const; /** Return a ledger entry. @return empty if the ledger entry does not exist */ - [[nodiscard]] std::shared_ptr + [[nodiscard]] SLE::const_pointer le(Keylet const& k) const; /** Create a JTx from parameters. */ diff --git a/src/test/jtx/PathSet.h b/src/test/jtx/PathSet.h index cab31ea540..a391adcb1b 100644 --- a/src/test/jtx/PathSet.h +++ b/src/test/jtx/PathSet.h @@ -18,7 +18,7 @@ countOffers( Asset const& takerGets) { size_t count = 0; - forEachItem(*env.current(), account, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), account, [&](SLE::const_ref sle) { if (sle->getType() == ltOFFER && sle->getFieldAmount(sfTakerPays).asset() == takerPays && sle->getFieldAmount(sfTakerGets).asset() == takerGets) ++count; @@ -34,7 +34,7 @@ countOffers( STAmount const& takerGets) { size_t count = 0; - forEachItem(*env.current(), account, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), account, [&](SLE::const_ref sle) { if (sle->getType() == ltOFFER && sle->getFieldAmount(sfTakerPays) == takerPays && sle->getFieldAmount(sfTakerGets) == takerGets) ++count; diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index 011ac2e58d..27c54d830b 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -356,7 +356,7 @@ checkVL(Slice const& result, std::string const& expected) [[nodiscard]] inline bool -checkVL(std::shared_ptr const& sle, SField const& field, std::string const& expected) +checkVL(SLE::const_ref sle, SField const& field, std::string const& expected) { return strHex(expected) == strHex(sle->getFieldVL(field)); } diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 4b6955bafb..707e1338a7 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -280,13 +280,13 @@ Env::seq(Account const& account) const return sle->getFieldU32(sfSequence); } -std::shared_ptr +SLE::const_pointer Env::le(Account const& account) const { return le(keylet::account(account.id())); } -std::shared_ptr +SLE::const_pointer Env::le(Keylet const& k) const { return current()->read(k); diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index c784c074de..a8ec899c8a 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -402,7 +402,7 @@ expectOffers( { std::uint16_t cnt = 0; std::uint16_t matched = 0; - forEachItem(*env.current(), account, [&](std::shared_ptr const& sle) { + forEachItem(*env.current(), account, [&](SLE::const_ref sle) { if (!sle) return false; if (sle->getType() == ltOFFER) diff --git a/src/test/jtx/impl/owners.cpp b/src/test/jtx/impl/owners.cpp index cb2cd5af29..2ff93757f0 100644 --- a/src/test/jtx/impl/owners.cpp +++ b/src/test/jtx/impl/owners.cpp @@ -10,8 +10,6 @@ #include #include -#include - namespace xrpl { namespace detail { @@ -19,7 +17,7 @@ std::uint32_t ownedCountOf(ReadView const& view, AccountID const& id, LedgerEntryType type) { std::uint32_t count = 0; - forEachItem(view, id, [&count, type](std::shared_ptr const& sle) { + forEachItem(view, id, [&count, type](SLE::const_ref sle) { if (sle->getType() == type) ++count; }); diff --git a/src/test/ledger/View_test.cpp b/src/test/ledger/View_test.cpp index d1c7316588..b62061d38d 100644 --- a/src/test/ledger/View_test.cpp +++ b/src/test/ledger/View_test.cpp @@ -61,7 +61,7 @@ class View_test : public beast::unit_test::Suite } // Create SLE with key and payload - static std::shared_ptr + static SLE::pointer sle(std::uint64_t id, std::uint32_t seq = 1) { auto const le = std::make_shared(k(id)); @@ -79,7 +79,7 @@ class View_test : public beast::unit_test::Suite // Set payload on SLE static void - seq(std::shared_ptr const& le, std::uint32_t seq) + seq(SLE::ref le, std::uint32_t seq) { le->setFieldU32(sfSequence, seq); } diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index 12c79b821c..54cf85ba35 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -4316,7 +4316,7 @@ NetworkOPsImp::getBookPage( bool bDone = false; bool bDirectAdvance = true; - std::shared_ptr sleOfferDir; + SLE::const_pointer sleOfferDir; uint256 offerIndex; unsigned int uBookEntry = 0; STAmount saDirRate; diff --git a/src/xrpld/app/misc/TxQ.h b/src/xrpld/app/misc/TxQ.h index ad689abed4..d3caec55cf 100644 --- a/src/xrpld/app/misc/TxQ.h +++ b/src/xrpld/app/misc/TxQ.h @@ -288,7 +288,7 @@ public: /** Return the next sequence that would go in the TxQ for an account. */ SeqProxy - nextQueuableSeq(std::shared_ptr const& sleAccount) const; + nextQueuableSeq(SLE::const_ref sleAccount) const; /** Returns fee metrics in reference fee level units. */ @@ -342,9 +342,7 @@ public: private: // Implementation for nextQueuableSeq(). The passed lock must be held. SeqProxy - nextQueuableSeqImpl( - std::shared_ptr const& sleAccount, - std::scoped_lock const&) const; + nextQueuableSeqImpl(SLE::const_ref sleAccount, std::scoped_lock const&) const; /** Track and use the fee escalation metrics of the @@ -782,7 +780,7 @@ private: STTx const&, ApplyFlags const, OpenView const&, - std::shared_ptr const& sleAccount, + SLE::const_ref sleAccount, AccountMap::iterator const&, std::optional const&, std::scoped_lock const& lock); diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 0f70f17046..c12632875d 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -150,7 +150,7 @@ TxQ::FeeMetrics::update( // current size limit, use a limit that is // 90% of the way from max_element to the // current size limit. - return (txnsExpected_ * 9 + *iter) / 10; + return ((txnsExpected_ * 9) + *iter) / 10; }(); // Ledgers are processing in a timely manner, // so keep the limit high, but don't let it @@ -218,7 +218,7 @@ sumOfFirstSquares(std::size_t xIn) // in a ledger, this is the least of our problems. if (x >= (1 << 21)) return {false, std::numeric_limits::max()}; - return {true, (x * (x + 1) * (2 * x + 1)) / 6}; + return {true, (x * (x + 1) * ((2 * x) + 1)) / 6}; } // Unit tests for sumOfSquares() @@ -387,7 +387,7 @@ TxQ::canBeHeld( STTx const& tx, ApplyFlags const flags, OpenView const& view, - std::shared_ptr const& sleAccount, + SLE::const_ref sleAccount, AccountMap::iterator const& accountIter, std::optional const& replacementIter, std::scoped_lock const& lock) @@ -1576,7 +1576,7 @@ TxQ::accept(Application& app, OpenView& view) // // Acquires a lock and calls the implementation. SeqProxy -TxQ::nextQueuableSeq(std::shared_ptr const& sleAccount) const +TxQ::nextQueuableSeq(SLE::const_ref sleAccount) const { std::scoped_lock const lock(mutex_); return nextQueuableSeqImpl(sleAccount, lock); @@ -1589,9 +1589,7 @@ TxQ::nextQueuableSeq(std::shared_ptr const& sleAccount) const // sequence number, that is not used by a transaction in the queue, must // be found and returned. SeqProxy -TxQ::nextQueuableSeqImpl( - std::shared_ptr const& sleAccount, - std::scoped_lock const&) const +TxQ::nextQueuableSeqImpl(SLE::const_ref sleAccount, std::scoped_lock const&) const { // If the account is not in the ledger or a non-account was passed // then return zero. We have no idea. diff --git a/src/xrpld/consensus/DisputedTx.h b/src/xrpld/consensus/DisputedTx.h index 1c85a3537d..1c0c069f54 100644 --- a/src/xrpld/consensus/DisputedTx.h +++ b/src/xrpld/consensus/DisputedTx.h @@ -286,7 +286,7 @@ DisputedTx::updateVote(int percentTime, bool proposing, ConsensusPar if (proposing) // give ourselves full weight { // This is basically the percentage of nodes voting 'yes' (including us) - weight = (yays_ * 100 + (ourVote_ ? 100 : 0)) / (nays_ + yays_ + 1); + weight = ((yays_ * 100) + (ourVote_ ? 100 : 0)) / (nays_ + yays_ + 1); newPosition = weight > requiredPct; } diff --git a/src/xrpld/rpc/detail/AssetCache.cpp b/src/xrpld/rpc/detail/AssetCache.cpp index a0743a2303..0976290d4c 100644 --- a/src/xrpld/rpc/detail/AssetCache.cpp +++ b/src/xrpld/rpc/detail/AssetCache.cpp @@ -124,7 +124,7 @@ AssetCache::getMPTs(xrpl::AccountID const& account) std::vector mpts; // Get issued/authorized tokens - forEachItem(*ledger_, account, [&](std::shared_ptr const& sle) { + forEachItem(*ledger_, account, [&](SLE::const_ref sle) { if (sle->getType() == ltMPTOKEN_ISSUANCE) { auto const mptID = makeMptID(sle->getFieldU32(sfSequence), account); diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index d48057a0a8..7ab2468b75 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,7 @@ namespace xrpl::RPC { std::uint64_t -getStartHint(std::shared_ptr const& sle, AccountID const& accountID) +getStartHint(SLE::const_ref sle, AccountID const& accountID) { if (sle->getType() == ltRIPPLE_STATE) { @@ -67,10 +66,7 @@ getStartHint(std::shared_ptr const& sle, AccountID const& accountID) } bool -isRelatedToAccount( - ReadView const& ledger, - std::shared_ptr const& sle, - AccountID const& accountID) +isRelatedToAccount(ReadView const& ledger, SLE::const_ref sle, AccountID const& accountID) { if (sle->getType() == ltRIPPLE_STATE) { diff --git a/src/xrpld/rpc/detail/RPCHelpers.h b/src/xrpld/rpc/detail/RPCHelpers.h index 781db1b8a5..bbc101a072 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.h +++ b/src/xrpld/rpc/detail/RPCHelpers.h @@ -33,7 +33,7 @@ struct JsonContext; * @return A 64-bit unsigned integer representing the start hint for traversal. */ std::uint64_t -getStartHint(std::shared_ptr const& sle, AccountID const& accountID); +getStartHint(SLE::const_ref sle, AccountID const& accountID); /** * @brief Tests if a ledger entry (SLE) is owned by the specified account. @@ -47,10 +47,7 @@ getStartHint(std::shared_ptr const& sle, AccountID const& accountID); * @return true if the SLE is owned by the account, false otherwise. */ bool -isRelatedToAccount( - ReadView const& ledger, - std::shared_ptr const& sle, - AccountID const& accountID); +isRelatedToAccount(ReadView const& ledger, SLE::const_ref sle, AccountID const& accountID); /** * @brief Parses an array of account IDs from a JSON value. diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index dd0e78c178..86d895fa1b 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -165,7 +165,7 @@ public: static ErrorCodeI acctMatchesPubKey( - std::shared_ptr accountState, + SLE::const_pointer accountState, AccountID const& accountID, PublicKey const& publicKey) { @@ -519,7 +519,7 @@ transactionPreProcessImpl( if (!verify && !txJson.isMember(jss::Sequence)) return RPC::missingFieldError("tx_json.Sequence"); - std::shared_ptr sle; + SLE::const_pointer sle; if (verify) sle = app.getOpenLedger().current()->read(keylet::account(srcAddressID)); @@ -1222,8 +1222,7 @@ transactionSignFor( signForParams.validMultiSign(), "xrpl::RPC::transactionSignFor : valid multi-signature"); { - std::shared_ptr const accountState = - ledger->read(keylet::account(*signerAccountID)); + SLE::const_pointer const accountState = ledger->read(keylet::account(*signerAccountID)); // Make sure the account and secret belong together. auto const err = acctMatchesPubKey(accountState, *signerAccountID, signForParams.getPublicKey()); @@ -1310,7 +1309,7 @@ transactionSubmitMultiSigned( if (RPC::containsError(txJsonResult)) return std::move(txJsonResult); - std::shared_ptr const sle = ledger->read(keylet::account(srcAddressID)); + SLE::const_pointer const sle = ledger->read(keylet::account(srcAddressID)); if (!sle) { diff --git a/src/xrpld/rpc/detail/TrustLine.cpp b/src/xrpld/rpc/detail/TrustLine.cpp index f7293d0816..77a2b36d56 100644 --- a/src/xrpld/rpc/detail/TrustLine.cpp +++ b/src/xrpld/rpc/detail/TrustLine.cpp @@ -9,13 +9,12 @@ #include #include -#include #include #include namespace xrpl { -TrustLineBase::TrustLineBase(std::shared_ptr const& sle, AccountID const& viewAccount) +TrustLineBase::TrustLineBase(SLE::const_ref sle, AccountID const& viewAccount) : key_(sle->key()) , lowLimit_(sle->getFieldAmount(sfLowLimit)) , highLimit_(sle->getFieldAmount(sfHighLimit)) @@ -37,7 +36,7 @@ TrustLineBase::getJson(int) } std::optional -PathFindTrustLine::makeItem(AccountID const& accountID, std::shared_ptr const& sle) +PathFindTrustLine::makeItem(AccountID const& accountID, SLE::const_ref sle) { if (!sle || sle->getType() != ltRIPPLE_STATE) return {}; @@ -53,14 +52,11 @@ getTrustLineItems( LineDirection direction = LineDirection::Outgoing) { std::vector items; - forEachItem( - view, - accountID, - [&items, &accountID, &direction](std::shared_ptr const& sleCur) { - auto ret = T::makeItem(accountID, sleCur); - if (ret && (direction == LineDirection::Outgoing || !ret->getNoRipple())) - items.push_back(std::move(*ret)); - }); + forEachItem(view, accountID, [&items, &accountID, &direction](SLE::const_ref sleCur) { + auto ret = T::makeItem(accountID, sleCur); + if (ret && (direction == LineDirection::Outgoing || !ret->getNoRipple())) + items.push_back(std::move(*ret)); + }); // This list may be around for a while, so free up any unneeded // capacity items.shrink_to_fit(); @@ -78,7 +74,7 @@ PathFindTrustLine::getItems( return detail::getTrustLineItems(accountID, view, direction); } -RPCTrustLine::RPCTrustLine(std::shared_ptr const& sle, AccountID const& viewAccount) +RPCTrustLine::RPCTrustLine(SLE::const_ref sle, AccountID const& viewAccount) : TrustLineBase(sle, viewAccount) , lowQualityIn_(sle->getFieldU32(sfLowQualityIn)) , lowQualityOut_(sle->getFieldU32(sfLowQualityOut)) @@ -88,7 +84,7 @@ RPCTrustLine::RPCTrustLine(std::shared_ptr const& sle, AccountID cons } std::optional -RPCTrustLine::makeItem(AccountID const& accountID, std::shared_ptr const& sle) +RPCTrustLine::makeItem(AccountID const& accountID, SLE::const_ref sle) { if (!sle || sle->getType() != ltRIPPLE_STATE) return {}; diff --git a/src/xrpld/rpc/detail/TrustLine.h b/src/xrpld/rpc/detail/TrustLine.h index 72d4d44ae3..7a0a01d744 100644 --- a/src/xrpld/rpc/detail/TrustLine.h +++ b/src/xrpld/rpc/detail/TrustLine.h @@ -39,7 +39,7 @@ public: protected: // This class should not be instantiated directly. Use one of the derived // classes. - TrustLineBase(std::shared_ptr const& sle, AccountID const& viewAccount); + TrustLineBase(SLE::const_ref sle, AccountID const& viewAccount); ~TrustLineBase() = default; TrustLineBase(TrustLineBase const&) = default; @@ -175,7 +175,7 @@ public: PathFindTrustLine() = delete; static std::optional - makeItem(AccountID const& accountID, std::shared_ptr const& sle); + makeItem(AccountID const& accountID, SLE::const_ref sle); static std::vector getItems(AccountID const& accountID, ReadView const& view, LineDirection direction); @@ -190,7 +190,7 @@ class RPCTrustLine final : public TrustLineBase, public CountedObject const& sle, AccountID const& viewAccount); + RPCTrustLine(SLE::const_ref sle, AccountID const& viewAccount); [[nodiscard]] Rate const& getQualityIn() const @@ -205,7 +205,7 @@ public: } static std::optional - makeItem(AccountID const& accountID, std::shared_ptr const& sle); + makeItem(AccountID const& accountID, SLE::const_ref sle); static std::vector getItems(AccountID const& accountID, ReadView const& view); diff --git a/src/xrpld/rpc/handlers/account/AccountChannels.cpp b/src/xrpld/rpc/handlers/account/AccountChannels.cpp index 6d5876322c..8a5c7dc6e3 100644 --- a/src/xrpld/rpc/handlers/account/AccountChannels.cpp +++ b/src/xrpld/rpc/handlers/account/AccountChannels.cpp @@ -114,7 +114,7 @@ doAccountChannels(RPC::JsonContext& context) json::Value jsonChannels{json::ValueType::Array}; struct VisitData { - std::vector> items; + std::vector items; AccountID const& accountID; std::optional const& raDstAccount; }; @@ -170,8 +170,7 @@ doAccountChannels(RPC::JsonContext& context) startAfter, startHint, limit + 1, - [&visitData, &accountID, &count, &limit, &marker, &nextHint]( - std::shared_ptr const& sleCur) { + [&visitData, &accountID, &count, &limit, &marker, &nextHint](SLE::const_ref sleCur) { if (!sleCur) { // LCOV_EXCL_START diff --git a/src/xrpld/rpc/handlers/account/AccountLines.cpp b/src/xrpld/rpc/handlers/account/AccountLines.cpp index c60ce90201..e69f70ca5a 100644 --- a/src/xrpld/rpc/handlers/account/AccountLines.cpp +++ b/src/xrpld/rpc/handlers/account/AccountLines.cpp @@ -195,8 +195,7 @@ doAccountLines(RPC::JsonContext& context) startAfter, startHint, limit + 1, - [&visitData, &count, &marker, &limit, &nextHint]( - std::shared_ptr const& sleCur) { + [&visitData, &count, &marker, &limit, &nextHint](SLE::const_ref sleCur) { if (!sleCur) { // LCOV_EXCL_START diff --git a/src/xrpld/rpc/handlers/account/AccountOffers.cpp b/src/xrpld/rpc/handlers/account/AccountOffers.cpp index 85d9470b75..4829ff56b1 100644 --- a/src/xrpld/rpc/handlers/account/AccountOffers.cpp +++ b/src/xrpld/rpc/handlers/account/AccountOffers.cpp @@ -33,7 +33,7 @@ namespace xrpl { void -appendOfferJson(std::shared_ptr const& offer, json::Value& offers) +appendOfferJson(SLE::const_ref offer, json::Value& offers) { STAmount const dirRate = amountFromQuality(getQuality(offer->getFieldH256(sfBookDirectory))); json::Value& obj(offers.append(json::ValueType::Object)); @@ -87,7 +87,7 @@ doAccountOffers(RPC::JsonContext& context) return *err; json::Value& jsonOffers(result[jss::offers] = json::ValueType::Array); - std::vector> offers; + std::vector offers; uint256 startAfter = beast::kZero; std::uint64_t startHint = 0; @@ -138,8 +138,7 @@ doAccountOffers(RPC::JsonContext& context) startAfter, startHint, limit + 1, - [&offers, &count, &marker, &limit, &nextHint, &accountID]( - std::shared_ptr const& sle) { + [&offers, &count, &marker, &limit, &nextHint, &accountID](SLE::const_ref sle) { if (!sle) { // LCOV_EXCL_START diff --git a/src/xrpld/rpc/handlers/account/GatewayBalances.cpp b/src/xrpld/rpc/handlers/account/GatewayBalances.cpp index 146b9ead5c..bd1681172c 100644 --- a/src/xrpld/rpc/handlers/account/GatewayBalances.cpp +++ b/src/xrpld/rpc/handlers/account/GatewayBalances.cpp @@ -144,7 +144,7 @@ doGatewayBalances(RPC::JsonContext& context) // Traverse the cold wallet's trust lines { - forEachItem(*ledger, accountID, [&](std::shared_ptr const& sle) { + forEachItem(*ledger, accountID, [&](SLE::const_ref sle) { if (sle->getType() == ltESCROW) { auto const& escrow = sle->getFieldAmount(sfAmount); diff --git a/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp b/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp index bb48d3ebd5..d8bb65aba9 100644 --- a/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp +++ b/src/xrpld/rpc/handlers/account/NoRippleCheck.cpp @@ -137,52 +137,50 @@ doNoRippleCheck(RPC::JsonContext& context) } } - forEachItemAfter( - *ledger, accountID, uint256(), 0, limit, [&](std::shared_ptr const& ownedItem) { - if (ownedItem->getType() == ltRIPPLE_STATE) + forEachItemAfter(*ledger, accountID, uint256(), 0, limit, [&](SLE::const_ref ownedItem) { + if (ownedItem->getType() == ltRIPPLE_STATE) + { + bool const bLow = accountID == ownedItem->getFieldAmount(sfLowLimit).getIssuer(); + + bool const bNoRipple = ownedItem->isFlag(bLow ? lsfLowNoRipple : lsfHighNoRipple); + + std::string problem; + bool needFix = false; + if (bNoRipple && roleGateway) { - bool const bLow = accountID == ownedItem->getFieldAmount(sfLowLimit).getIssuer(); - - bool const bNoRipple = ownedItem->isFlag(bLow ? lsfLowNoRipple : lsfHighNoRipple); - - std::string problem; - bool needFix = false; - if (bNoRipple && roleGateway) - { - problem = "You should clear the no ripple flag on your "; - needFix = true; - } - else if (!roleGateway && !bNoRipple) - { - problem = "You should probably set the no ripple flag on your "; - needFix = true; - } - if (needFix) - { - AccountID const peer = - ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit).getIssuer(); - STAmount const peerLimit = - ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit); - problem += to_string(peerLimit.get().currency); - problem += " line to "; - problem += to_string(peerLimit.getIssuer()); - problems.append(problem); - - STAmount limitAmount( - ownedItem->getFieldAmount(bLow ? sfLowLimit : sfHighLimit)); - limitAmount.get().account = peer; - - json::Value& tx = jvTransactions.append(json::ValueType::Object); - tx["TransactionType"] = jss::TrustSet; - tx["LimitAmount"] = limitAmount.getJson(JsonOptions::Values::None); - tx["Flags"] = bNoRipple ? tfClearNoRipple : tfSetNoRipple; - fillTransaction(context, tx, accountID, seq, *ledger); - - return true; - } + problem = "You should clear the no ripple flag on your "; + needFix = true; } - return false; - }); + else if (!roleGateway && !bNoRipple) + { + problem = "You should probably set the no ripple flag on your "; + needFix = true; + } + if (needFix) + { + AccountID const peer = + ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit).getIssuer(); + STAmount const peerLimit = + ownedItem->getFieldAmount(bLow ? sfHighLimit : sfLowLimit); + problem += to_string(peerLimit.get().currency); + problem += " line to "; + problem += to_string(peerLimit.getIssuer()); + problems.append(problem); + + STAmount limitAmount(ownedItem->getFieldAmount(bLow ? sfLowLimit : sfHighLimit)); + limitAmount.get().account = peer; + + json::Value& tx = jvTransactions.append(json::ValueType::Object); + tx["TransactionType"] = jss::TrustSet; + tx["LimitAmount"] = limitAmount.getJson(JsonOptions::Values::None); + tx["Flags"] = bNoRipple ? tfClearNoRipple : tfSetNoRipple; + fillTransaction(context, tx, accountID, seq, *ledger); + + return true; + } + } + return false; + }); return result; } diff --git a/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp index df6772e4c0..b9f4a42880 100644 --- a/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp +++ b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp @@ -76,7 +76,7 @@ doAMMInfo(RPC::JsonContext& context) std::optional accountID; Asset asset1; Asset asset2; - std::shared_ptr amm; + SLE::const_pointer amm; }; auto getValuesFromContextParams = [&]() -> Expected { diff --git a/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp b/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp index cc176aaadb..343d539277 100644 --- a/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp +++ b/src/xrpld/rpc/handlers/orderbook/DepositAuthorized.cpp @@ -90,7 +90,7 @@ doDepositAuthorized(RPC::JsonContext& context) bool const credentialsPresent = params.isMember(jss::credentials); std::set> sorted; - std::vector> lifeExtender; + std::vector lifeExtender; if (credentialsPresent) { auto const& creds(params[jss::credentials]); @@ -128,7 +128,7 @@ doDepositAuthorized(RPC::JsonContext& context) jss::credentials, "an array of CredentialID(hash256)")); } - std::shared_ptr sleCred = ledger->read(keylet::credential(credH)); + SLE::const_pointer sleCred = ledger->read(keylet::credential(credH)); if (!sleCred) { RPC::injectError(RpcBadCredentials, "credentials don't exist", result); diff --git a/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp index ae551de1ab..6a75277b1b 100644 --- a/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp +++ b/src/xrpld/rpc/handlers/orderbook/GetAggregatePrice.cpp @@ -48,7 +48,7 @@ using Prices = static void iteratePriceData( RPC::JsonContext& context, - std::shared_ptr const& sle, + SLE::const_ref sle, std::function const& f) { static constexpr std::uint8_t kMaxHistory = 3; diff --git a/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h b/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h index b94e431117..8529ec2d2c 100644 --- a/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h +++ b/src/xrpld/rpc/handlers/orderbook/NFTOffersHelpers.h @@ -17,10 +17,7 @@ namespace xrpl { inline void -appendNftOfferJson( - Application const& app, - std::shared_ptr const& offer, - json::Value& offers) +appendNftOfferJson(Application const& app, SLE::const_ref offer, json::Value& offers) { json::Value& obj(offers.append(json::ValueType::Object)); @@ -64,7 +61,7 @@ enumerateNFTOffers(RPC::JsonContext& context, uint256 const& nftId, Keylet const json::Value& jsonOffers(result[jss::offers] = json::ValueType::Array); - std::vector> offers; + std::vector offers; unsigned int reserve(limit); uint256 startAfter; std::uint64_t startHint = 0; @@ -97,12 +94,7 @@ enumerateNFTOffers(RPC::JsonContext& context, uint256 const& nftId, Keylet const } if (!forEachItemAfter( - *ledger, - directory, - startAfter, - startHint, - reserve, - [&offers](std::shared_ptr const& offer) { + *ledger, directory, startAfter, startHint, reserve, [&offers](SLE::const_ref offer) { if (offer->getType() == ltNFTOKEN_OFFER) { offers.emplace_back(offer); diff --git a/src/xrpld/rpc/handlers/transaction/Simulate.cpp b/src/xrpld/rpc/handlers/transaction/Simulate.cpp index 7a11b728ce..676f0318a2 100644 --- a/src/xrpld/rpc/handlers/transaction/Simulate.cpp +++ b/src/xrpld/rpc/handlers/transaction/Simulate.cpp @@ -61,7 +61,7 @@ getAutofillSequence(json::Value const& txJson, RPC::JsonContext& context) return Unexpected( RPC::makeError(RpcSrcActMalformed, RPC::invalidFieldMessage("tx.Account"))); } - std::shared_ptr const sle = + SLE::const_pointer const sle = context.app.getOpenLedger().current()->read(keylet::account(*srcAddressID)); if (!hasTicketSeq && !sle) { From e209ee537167ef23f2889eec66640ef21e7ab0ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 11:29:12 -0400 Subject: [PATCH 51/87] ci: [DEPENDABOT] bump eps1lon/actions-label-merge-conflict from 3.0.3 to 3.1.0 (#7375) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/conflicting-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/conflicting-pr.yml b/.github/workflows/conflicting-pr.yml index 6e667632a0..772d46fd7d 100644 --- a/.github/workflows/conflicting-pr.yml +++ b/.github/workflows/conflicting-pr.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check if PRs are dirty - uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 + uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0 with: dirtyLabel: "PR: has conflicts" repoToken: "${{ secrets.GITHUB_TOKEN }}" From d4cb68d5a1904b19d220c7f681aaa31f7dd9ffbd Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 1 Jun 2026 17:47:01 +0100 Subject: [PATCH 52/87] ci: Check binaries separately from building them (#7355) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docker/check-sanitizers.sh | 48 -------------- docker/loader-path.sh | 12 ++++ docker/nix.Dockerfile | 47 ++++++++------ docker/test_files/compile-cpp-sources.sh | 50 +++++++++++++++ .../cpp_sources}/asan.cpp | 0 docker/test_files/cpp_sources/regular.cpp | 28 +++++++++ .../cpp_sources}/tsan.cpp | 0 .../cpp_sources}/ubsan.cpp | 0 docker/test_files/run-test-binaries.sh | 62 +++++++++++++++++++ 9 files changed, 179 insertions(+), 68 deletions(-) delete mode 100755 docker/check-sanitizers.sh create mode 100755 docker/loader-path.sh create mode 100755 docker/test_files/compile-cpp-sources.sh rename docker/{cpp_files => test_files/cpp_sources}/asan.cpp (100%) create mode 100644 docker/test_files/cpp_sources/regular.cpp rename docker/{cpp_files => test_files/cpp_sources}/tsan.cpp (100%) rename docker/{cpp_files => test_files/cpp_sources}/ubsan.cpp (100%) create mode 100755 docker/test_files/run-test-binaries.sh diff --git a/docker/check-sanitizers.sh b/docker/check-sanitizers.sh deleted file mode 100755 index 38ccaed560..0000000000 --- a/docker/check-sanitizers.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -# Sanity-check that the sanitizer runtimes shipped with g++/clang++ work -# end-to-end against the system loader: compile each example with both -# compilers, run it, and confirm the expected diagnostic is emitted. - -set -eo pipefail - -cpp_files_dir="${1:?usage: $0 }" - -case "$(uname -m)" in - x86_64) loader=/lib64/ld-linux-x86-64.so.2 ;; - aarch64) loader=/lib/ld-linux-aarch64.so.1 ;; - *) - echo "Unsupported arch: $(uname -m)" >&2 - exit 1 - ;; -esac - -declare -A sanitize=( - [asan]="-fsanitize=address" - [tsan]="-fsanitize=thread" - [ubsan]="-fsanitize=undefined" -) -declare -A expect=( - [asan]="heap-use-after-free" - [tsan]="data race" - [ubsan]="signed integer overflow" -) - -for compiler in g++ clang++; do - for name in asan tsan ubsan; do - bin="/tmp/${name}-${compiler}" - echo "=== Build ${name} with ${compiler} ===" - "$compiler" -std=c++20 -O1 -g ${sanitize[$name]} \ - -Wl,--dynamic-linker=$loader \ - "${cpp_files_dir}/${name}.cpp" -o "$bin" - echo "=== Run ${name}-${compiler} ===" - output=$("$bin" 2>&1) || true - echo "$output" - echo "$output" | grep -q "${expect[$name]}" || - { - echo "expected '${expect[$name]}' from $bin" - exit 1 - } - rm -f "$bin" - done -done diff --git a/docker/loader-path.sh b/docker/loader-path.sh new file mode 100755 index 0000000000..b8b9f0de51 --- /dev/null +++ b/docker/loader-path.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +case "$(uname -m)" in + x86_64) LOADER=/lib64/ld-linux-x86-64.so.2 ;; + aarch64) LOADER=/lib/ld-linux-aarch64.so.1 ;; + *) + echo "Unsupported arch: $(uname -m)" >&2 + exit 1 + ;; +esac + +echo "${LOADER}" diff --git a/docker/nix.Dockerfile b/docker/nix.Dockerfile index 690f0b76bd..3c5dbcb734 100644 --- a/docker/nix.Dockerfile +++ b/docker/nix.Dockerfile @@ -27,7 +27,9 @@ RUN mkdir /tmp/nix-store-closure && \ cp -R $(nix-store -qR result/) /tmp/nix-store-closure # Final image -FROM ${BASE_IMAGE} +FROM ${BASE_IMAGE} AS final + +ARG BASE_IMAGE # bash is not located at /bin/bash in nixos/nix, so we need to create a symlink to it. RUN if [ -d /nix ]; then \ @@ -43,25 +45,23 @@ ENTRYPOINT ["/bin/bash"] COPY --from=builder /tmp/nix-store-closure /nix/store COPY --from=builder /tmp/build/result /nix/ci-env -ENV PATH="/nix/ci-env/bin:$PATH" +ENV PATH="/nix/ci-env/bin:${PATH}" # Externally-built dynamically-linked ELF binaries hard-code the loader path -# (e.g. /lib64/ld-linux-x86-64.so.2) in their PT_INTERP header. Copy the -# loader from the Nix store to that path when the base image doesn't already -# provide one (i.e. on nixos/nix). +# (e.g. /lib64/ld-linux-x86-64.so.2) in their PT_INTERP header. Install it +# from the Nix store when the base image doesn't already provide one. +COPY docker/loader-path.sh /tmp/loader-path.sh + RUN <&2; exit 1 ;; -esac -if [ ! -e "$target" ]; then +target="$(/tmp/loader-path.sh)" + +if [ ! -e "${target}" ]; then # Use the loader from the same glibc that gcc links libc against, so # ld-linux and libc/libpthread share GLIBC_PRIVATE symbols at runtime. - src="$(dirname "$(gcc -print-file-name=libc.so.6)")/$(basename "$target")" - [ -e "$src" ] || { echo "ld-linux not found at $src" >&2; exit 1; } - mkdir -p "$(dirname "$target")" - cp "$src" "$target" + src="$(dirname "$(gcc -print-file-name=libc.so.6)")/$(basename "${target}")" + [ -e "${src}" ] || { echo "ld-linux not found at ${src}" >&2; exit 1; } + mkdir -p "$(dirname "${target}")" + cp "${src}" "${target}" fi EOF @@ -87,9 +87,16 @@ run-clang-tidy --help vim --version EOF -# Sanity-check that the sanitizer runtimes shipped with g++/clang++ work -# end-to-end against the system loader. -COPY docker/cpp_files/ /tmp/cpp_files/ -COPY docker/check-sanitizers.sh /tmp/check-sanitizers.sh +# Sanity-check that the sanitizer runtimes shipped with g++/clang++ are able to build binaries +COPY docker/test_files/cpp_sources/ /tmp/cpp_sources/ +COPY docker/test_files/compile-cpp-sources.sh /tmp/compile-cpp-sources.sh +RUN /tmp/compile-cpp-sources.sh /tmp/cpp_sources /tmp/bins -RUN grep -qi ubuntu /etc/os-release 2>/dev/null && /tmp/check-sanitizers.sh /tmp/cpp_files || true +# Sanity-check that the built binaries are able to run. +# We only support running the test binaries on Ubuntu and NixOS right now (will be fixed in the future) +# +# When build and test images will be separate, we will be to run on vanilla images. +COPY docker/test_files/run-test-binaries.sh /tmp/run-test-binaries.sh +RUN if echo "${BASE_IMAGE}" | grep -qiE '(ubuntu|nixos)'; then \ + /tmp/run-test-binaries.sh /tmp/bins; \ + fi diff --git a/docker/test_files/compile-cpp-sources.sh b/docker/test_files/compile-cpp-sources.sh new file mode 100755 index 0000000000..b4edaee2f6 --- /dev/null +++ b/docker/test_files/compile-cpp-sources.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Compile all C++ test binaries during the Docker image build. +# Each binary has the target system's ELF PT_INTERP (dynamic-linker path) +# baked in so it can run on the (potentially minimal) final BASE_IMAGE. + +set -eo pipefail + +src_dir="${1:?usage: $0 }" +dst_dir="${2:?usage: $0 }" + +loader="$(/tmp/loader-path.sh)" + +mkdir -p "${dst_dir}" + +function compile() { + local compiler="${1}" + local name="${2}" + local san_flag="${3:-}" + + local src="${src_dir}/${name}.cpp" + local binary="${dst_dir}/${name}-${compiler}" + + echo "=== Compile ${name} with ${compiler} ===" + cmd="${compiler} -std=c++23 -O1 -g \ + -pthread \ + -Wl,--dynamic-linker=${loader} \ + ${san_flag} \ + ${src} -o ${binary}" + echo "Command: ${cmd}" + eval "${cmd}" +} + +declare -A sanitize=( + [regular]="" + + [asan]="-fsanitize=address" + [tsan]="-fsanitize=thread" + [ubsan]="-fsanitize=undefined -fno-sanitize-recover=all" +) + +for name in regular asan tsan ubsan; do + san_flag="${sanitize[${name}]}" + for compiler in g++ clang++; do + compile "${compiler}" "${name}" "${san_flag}" + done +done + +echo "=== All binaries compiled ===" + +ls -la "${dst_dir}" diff --git a/docker/cpp_files/asan.cpp b/docker/test_files/cpp_sources/asan.cpp similarity index 100% rename from docker/cpp_files/asan.cpp rename to docker/test_files/cpp_sources/asan.cpp diff --git a/docker/test_files/cpp_sources/regular.cpp b/docker/test_files/cpp_sources/regular.cpp new file mode 100644 index 0000000000..637dafa1fd --- /dev/null +++ b/docker/test_files/cpp_sources/regular.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +static std::mutex gMutex; + +void +worker(int id) +{ + std::lock_guard lock(gMutex); + std::cout << "Hello from thread " << id << "\n"; +} + +int +main() +{ + constexpr int kNumThreads = 10; + std::vector threads; + threads.reserve(kNumThreads); + for (int i = 0; i < kNumThreads; ++i) + threads.emplace_back(worker, i); + for (auto& t : threads) + t.join(); + + std::cout << "Hello from main thread\n"; + return 0; +} diff --git a/docker/cpp_files/tsan.cpp b/docker/test_files/cpp_sources/tsan.cpp similarity index 100% rename from docker/cpp_files/tsan.cpp rename to docker/test_files/cpp_sources/tsan.cpp diff --git a/docker/cpp_files/ubsan.cpp b/docker/test_files/cpp_sources/ubsan.cpp similarity index 100% rename from docker/cpp_files/ubsan.cpp rename to docker/test_files/cpp_sources/ubsan.cpp diff --git a/docker/test_files/run-test-binaries.sh b/docker/test_files/run-test-binaries.sh new file mode 100755 index 0000000000..6e8f0a931c --- /dev/null +++ b/docker/test_files/run-test-binaries.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Run pre-compiled sanitizer binaries and confirm each emits its expected diagnostic. +# Binaries must already exist in with the layout: +# -g++ and -clang++ for name in {regular,asan,tsan,ubsan} + +set -eo pipefail + +bins_dir="${1:?usage: $0 }" + +# Run a binary and verify its exit code and output. +# Usage: run +function run() { + local binary="${1}" + local expected_output="${2}" + local expected_rc="${3}" + + local out_file + out_file="$(mktemp)" + + echo "=== Run ${binary} ===" + local rc=0 + "${binary}" >"${out_file}" 2>&1 || rc=$? + + cat "${out_file}" + + if [ "${expected_rc}" = "nonzero" ]; then + if [ "${rc}" -eq 0 ]; then + echo "ERROR: expected non-zero exit code from ${binary}, got ${rc}" >&2 + exit 1 + fi + elif [ "${rc}" -ne "${expected_rc}" ]; then + echo "ERROR: expected exit code ${expected_rc} from ${binary}, got ${rc}" >&2 + exit 1 + fi + + grep -q "${expected_output}" "${out_file}" || + { + echo "ERROR: expected '${expected_output}' from ${binary}" >&2 + exit 1 + } + echo "OK: '${expected_output}' detected" +} + +declare -A expect=( + [regular]="Hello from main thread" + + [asan]="heap-use-after-free" + [tsan]="data race" + [ubsan]="signed integer overflow" +) + +for compiler in g++ clang++; do + for name in regular asan tsan ubsan; do + binary="${bins_dir}/${name}-${compiler}" + if [ "${name}" = "regular" ]; then + expected_rc=0 + else + expected_rc=nonzero + fi + run "${binary}" "${expect[$name]}" "${expected_rc}" + done +done From ad111bcc22d439c4bc6d40333aa0db9f6c8e0d64 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 2 Jun 2026 14:51:20 +0100 Subject: [PATCH 53/87] ci: Patch binaries in nix-based images and test in every distro (#7376) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docker/check-tool-versions.sh | 24 +++++++ docker/install-sanitizer-libs.sh | 89 ++++++++++++++++++++++++ docker/nix.Dockerfile | 66 ++++++++++-------- docker/test_files/compile-cpp-sources.sh | 17 +++-- docker/test_files/run-test-binaries.sh | 44 +++++++++--- nix/packages.nix | 1 + 6 files changed, 196 insertions(+), 45 deletions(-) create mode 100755 docker/check-tool-versions.sh create mode 100755 docker/install-sanitizer-libs.sh diff --git a/docker/check-tool-versions.sh b/docker/check-tool-versions.sh new file mode 100755 index 0000000000..db20be45e4 --- /dev/null +++ b/docker/check-tool-versions.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Verify that every tool expected in the Nix CI env is present and runnable. +set -euo pipefail + +ccache --version +clang --version +clang++ --version +clang-format --version +cmake --version +conan --version +g++ --version +gcc --version +gcovr --version +git --version +less --version +make --version +mold --version +ninja --version +perl --version +pkg-config --version +pre-commit --version +python3 --version +run-clang-tidy --help +vim --version diff --git a/docker/install-sanitizer-libs.sh b/docker/install-sanitizer-libs.sh new file mode 100755 index 0000000000..a28efeab3f --- /dev/null +++ b/docker/install-sanitizer-libs.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# Install sanitizer runtime libraries required to run binaries compiled with: +# -fsanitize=address → libasan.so.8 +# -fsanitize=thread → libtsan.so.2 +# -fsanitize=undefined → libubsan.so.1 +# +# The exact SONAMEs required depend on the compiler toolchain used to build the +# test binaries (see nix/ci-env.nix). If the toolchain is bumped and SONAMEs +# change, update the list below (or detect them from the binaries). +# +# Supported base images: +# debian:bookworm +# ubuntu:20.04 +# rhel:9 +# nixos/nix — tests are skipped; this script is not called + +set -euo pipefail + +if [ ! -f /etc/os-release ]; then + echo "ERROR: /etc/os-release not found; cannot detect OS" >&2 + exit 1 +fi + +# shellcheck source=/dev/null +. /etc/os-release + +echo "Detected OS: ${ID} ${VERSION_ID:-}" + +case "${ID}" in + debian) + apt-get update -y + apt-get install -y --no-install-recommends \ + libasan8 \ + libtsan2 \ + libubsan1 + + apt-get clean + rm -rf /var/lib/apt/lists/* + ;; + + ubuntu) + apt-get update -y + apt-get install -y --no-install-recommends \ + gnupg \ + software-properties-common + add-apt-repository -y ppa:ubuntu-toolchain-r/test + apt-get update -y + apt-get install -y --no-install-recommends \ + libasan8 \ + libtsan2 \ + libubsan1 + + apt-get clean + rm -rf /var/lib/apt/lists/* + ;; + + rhel | centos | rocky | almalinux) + dnf install -y \ + libasan8 \ + libtsan2 \ + libubsan + + dnf clean -y all + rm -rf /var/cache/dnf/* + ;; + + *) + echo "ERROR: unsupported OS '${ID}'. Supported: debian, ubuntu, rhel-family" >&2 + exit 1 + ;; +esac + +# Verify that every expected library is now resolvable by the dynamic linker. +missing=0 +for lib in libasan.so.8 libtsan.so.2 libubsan.so.1; do + if ldconfig -p | grep -q "${lib}"; then + echo "OK: ${lib} found" + else + echo "ERROR: ${lib} not found after installation" >&2 + missing=$((missing + 1)) + fi +done + +if [ "${missing}" -ne 0 ]; then + echo "ERROR: ${missing} library/libraries missing" >&2 + exit 1 +fi + +echo "All sanitizer runtime libraries installed successfully." diff --git a/docker/nix.Dockerfile b/docker/nix.Dockerfile index 3c5dbcb734..a0eab31769 100644 --- a/docker/nix.Dockerfile +++ b/docker/nix.Dockerfile @@ -32,7 +32,7 @@ FROM ${BASE_IMAGE} AS final ARG BASE_IMAGE # bash is not located at /bin/bash in nixos/nix, so we need to create a symlink to it. -RUN if [ -d /nix ]; then \ +RUN if echo "${BASE_IMAGE}" | grep -qiE 'nixos'; then \ ln -s /root/.nix-profile/bin/bash /bin/bash; \ fi @@ -65,38 +65,44 @@ if [ ! -e "${target}" ]; then fi EOF -RUN < function run() { @@ -18,27 +20,34 @@ function run() { out_file="$(mktemp)" echo "=== Run ${binary} ===" - local rc=0 - "${binary}" >"${out_file}" 2>&1 || rc=$? + set +e + "${binary}" >"${out_file}" 2>&1 + local rc=$? + set -e cat "${out_file}" + local failed=0 if [ "${expected_rc}" = "nonzero" ]; then if [ "${rc}" -eq 0 ]; then echo "ERROR: expected non-zero exit code from ${binary}, got ${rc}" >&2 - exit 1 + failed=1 fi elif [ "${rc}" -ne "${expected_rc}" ]; then echo "ERROR: expected exit code ${expected_rc} from ${binary}, got ${rc}" >&2 - exit 1 + failed=1 fi - grep -q "${expected_output}" "${out_file}" || - { - echo "ERROR: expected '${expected_output}' from ${binary}" >&2 - exit 1 - } - echo "OK: '${expected_output}' detected" + if ! grep -q "${expected_output}" "${out_file}"; then + echo "ERROR: expected '${expected_output}' from ${binary}" >&2 + failed=1 + fi + + if [ "${failed}" -eq 0 ]; then + echo "OK: '${expected_output}' detected" + else + failed_binaries+=("${binary}") + fi } declare -A expect=( @@ -52,6 +61,15 @@ declare -A expect=( for compiler in g++ clang++; do for name in regular asan tsan ubsan; do binary="${bins_dir}/${name}-${compiler}" + + if [ "${name}" = "tsan" ] && [ "${compiler}" = "g++" ] && + grep -qi 'debian' /etc/os-release 2>/dev/null && + [ "$(uname -m)" = "aarch64" ]; then + echo "=== Skipping ${binary} (tsan-g++ unsupported on Debian ARM64) ===" + echo " NOTE: to enable it, add --security-opt seccomp=unconfined to your docker run command" + continue + fi + if [ "${name}" = "regular" ]; then expected_rc=0 else @@ -60,3 +78,9 @@ for compiler in g++ clang++; do run "${binary}" "${expect[$name]}" "${expected_rc}" done done + +if [ "${#failed_binaries[@]}" -gt 0 ]; then + echo "ERROR: the following binaries failed:" >&2 + printf ' %s\n' "${failed_binaries[@]}" >&2 + exit 1 +fi diff --git a/nix/packages.nix b/nix/packages.nix index d209620a68..3d92fedb4b 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -15,6 +15,7 @@ in git gnumake llvmPackages_22.clang-tools + less # needed for git diff mold ninja patchelf From 225ed204ad101527c19f1f3c9a32fda1bd28761b Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 2 Jun 2026 19:12:09 +0200 Subject: [PATCH 54/87] test: Suppress invariant-failure logs in Vault and LoanBroker bug-regression tests (#7379) --- src/test/app/LoanBroker_test.cpp | 5 ++++- src/test/app/Vault_test.cpp | 26 ++++++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index 92949256fd..0edb955b90 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -52,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -1708,7 +1710,8 @@ class LoanBroker_test : public beast::unit_test::Suite Account const alice("alice"); auto const withFix = features[fixCleanup3_2_0]; - Env env(*this, features); + std::string logs; + Env env(*this, features, std::make_unique(&logs)); env.fund(XRP(100'000), issuer, alice); env.close(); diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index bf707afae9..2c83ad91ec 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -6630,7 +6632,8 @@ class Vault_test : public beast::unit_test::Suite "fixed-asset amount with impaired loan"} + (withFix ? " (fixCleanup3_2_0)" : " (pre-fix)")); - Env env(*this, features); + std::string logs; + Env env(*this, features, std::make_unique(&logs)); auto const f = setupStuckDepositor(env); if (!f.vaultKeylet || !f.asset || f.sharesLender == 0) { @@ -6748,7 +6751,8 @@ class Vault_test : public beast::unit_test::Suite "burn is rejected while loss outstanding"} + (withFix ? " (fixCleanup3_2_0)" : " (pre-fix)")); - Env env(*this, features); + std::string logs; + Env env(*this, features, std::make_unique(&logs)); auto const f = setupStuckDepositor(env); if (!f.vaultKeylet || f.sharesLender == 0) { @@ -7074,7 +7078,8 @@ class Vault_test : public beast::unit_test::Suite using namespace test::jtx; auto runScenario = [this](FeatureBitset features, TER expected) { - Env env(*this, features); + std::string logs; + Env env(*this, features, std::make_unique(&logs)); Account const issuer{"issuer"}; Account const alice{"alice"}; @@ -7150,7 +7155,8 @@ class Vault_test : public beast::unit_test::Suite using namespace test::jtx; auto runScenario = [this](FeatureBitset features, TER expected) { - Env env(*this, features); + std::string logs; + Env env(*this, features, std::make_unique(&logs)); Account const issuer{"issuer"}; Account const alice{"alice"}; @@ -7226,7 +7232,8 @@ class Vault_test : public beast::unit_test::Suite enum class DestKind : bool { ThirdParty = false, Self = true }; auto runScenario = [this](FeatureBitset features, DestKind destKind, TER expected) { - Env env(*this, features); + std::string logs; + Env env(*this, features, std::make_unique(&logs)); Account const issuer{"issuer"}; Account const alice{"alice"}; @@ -7331,7 +7338,8 @@ class Vault_test : public beast::unit_test::Suite using namespace test::jtx; auto runScenario = [this](FeatureBitset features, TER expected) { - Env env(*this, features); + std::string logs; + Env env(*this, features, std::make_unique(&logs)); Account const issuer{"issuer"}; Account const alice{"alice"}; @@ -7414,7 +7422,8 @@ class Vault_test : public beast::unit_test::Suite { using namespace test::jtx; auto runScenario = [this](FeatureBitset features, TER expected) { - Env env(*this, features); + std::string logs; + Env env(*this, features, std::make_unique(&logs)); Account const issuer{"issuer"}; Account const alice{"alice"}; @@ -7489,7 +7498,8 @@ class Vault_test : public beast::unit_test::Suite using namespace test::jtx; auto runScenario = [this](FeatureBitset features, TER expected) { - Env env(*this, features); + std::string logs; + Env env(*this, features, std::make_unique(&logs)); Account const issuer{"issuer"}; Account const owner{"owner"}; From 1441d4690d91cf047704af27cca0381269c8b17e Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 3 Jun 2026 01:16:02 +0100 Subject: [PATCH 55/87] chore: Update flake.lock to allow conan with clang-22 support (#7390) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 3149f3feed..2013cfabd4 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1777954456, - "narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", + "lastModified": 1780243769, + "narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", + "rev": "331800de5053fcebacf6813adb5db9c9dca22a0c", "type": "github" }, "original": { From 96b2c0964f0d19f48853fd3fcec362e9bfba5d60 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 3 Jun 2026 11:34:19 -0400 Subject: [PATCH 56/87] refactor: Replace `intr_ptr::SharedPtr` by `SHAMapTreeNodePtr` (#7396) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- include/xrpl/shamap/SHAMap.h | 57 ++++++++----------- .../xrpl/shamap/SHAMapAccountStateLeafNode.h | 2 +- include/xrpl/shamap/SHAMapInnerNode.h | 16 +++--- include/xrpl/shamap/SHAMapTreeNode.h | 15 +++-- include/xrpl/shamap/SHAMapTxLeafNode.h | 2 +- .../xrpl/shamap/SHAMapTxPlusMetaLeafNode.h | 2 +- include/xrpl/shamap/TreeNodeCache.h | 2 +- include/xrpl/shamap/detail/TaggedPointer.h | 4 +- include/xrpl/shamap/detail/TaggedPointer.ipp | 38 ++++++------- src/libxrpl/shamap/SHAMap.cpp | 57 ++++++++----------- src/libxrpl/shamap/SHAMapDelta.cpp | 7 +-- src/libxrpl/shamap/SHAMapInnerNode.cpp | 26 +++++---- src/libxrpl/shamap/SHAMapSync.cpp | 8 +-- src/libxrpl/shamap/SHAMapTreeNode.cpp | 10 ++-- 14 files changed, 115 insertions(+), 131 deletions(-) diff --git a/include/xrpl/shamap/SHAMap.h b/include/xrpl/shamap/SHAMap.h index f63fc95b27..32e87b64c6 100644 --- a/include/xrpl/shamap/SHAMap.h +++ b/include/xrpl/shamap/SHAMap.h @@ -85,7 +85,7 @@ private: /** The sequence of the ledger that this map references, if any. */ std::uint32_t ledgerSeq_ = 0; - intr_ptr::SharedPtr root_; + SHAMapTreeNodePtr root_; mutable SHAMapState state_; SHAMapType const type_; bool backed_ = true; // Map is backed by the database @@ -326,36 +326,32 @@ public: invariants() const; private: - using SharedPtrNodeStack = - std::stack, SHAMapNodeID>>; + using SharedPtrNodeStack = std::stack>; using DeltaRef = std::pair, boost::intrusive_ptr>; // tree node cache operations - intr_ptr::SharedPtr + SHAMapTreeNodePtr cacheLookup(SHAMapHash const& hash) const; void - canonicalize(SHAMapHash const& hash, intr_ptr::SharedPtr&) const; + canonicalize(SHAMapHash const& hash, SHAMapTreeNodePtr&) const; // database operations - intr_ptr::SharedPtr + SHAMapTreeNodePtr fetchNodeFromDB(SHAMapHash const& hash) const; - intr_ptr::SharedPtr + SHAMapTreeNodePtr fetchNodeNT(SHAMapHash const& hash) const; - intr_ptr::SharedPtr + SHAMapTreeNodePtr fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const; - intr_ptr::SharedPtr + SHAMapTreeNodePtr fetchNode(SHAMapHash const& hash) const; - intr_ptr::SharedPtr + SHAMapTreeNodePtr checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const; /** Update hashes up to the root */ void - dirtyUp( - SharedPtrNodeStack& stack, - uint256 const& target, - intr_ptr::SharedPtr terminal); + 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() == @@ -377,25 +373,21 @@ private: preFlushNode(intr_ptr::SharedPtr node) const; /** write and canonicalize modified node */ - intr_ptr::SharedPtr - writeNode(NodeObjectType t, intr_ptr::SharedPtr node) const; + SHAMapTreeNodePtr + writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const; // returns the first item at or below this node SHAMapLeafNode* - firstBelow(intr_ptr::SharedPtr, SharedPtrNodeStack& stack, int branch = 0) - const; + firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch = 0) const; // returns the last item at or below this node SHAMapLeafNode* - lastBelow( - intr_ptr::SharedPtr node, - SharedPtrNodeStack& stack, - int branch = kBranchFactor) const; + lastBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch = kBranchFactor) const; // helper function for firstBelow and lastBelow SHAMapLeafNode* belowHelper( - intr_ptr::SharedPtr node, + SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch, std::tuple, std::function> const& loopParams) @@ -407,15 +399,14 @@ private: descend(SHAMapInnerNode*, int branch) const; SHAMapTreeNode* descendThrow(SHAMapInnerNode*, int branch) const; - intr_ptr::SharedPtr + SHAMapTreeNodePtr descend(SHAMapInnerNode&, int branch) const; - intr_ptr::SharedPtr + SHAMapTreeNodePtr descendThrow(SHAMapInnerNode&, int branch) const; // Descend with filter // If pending, callback is called as if it called fetchNodeNT - using descendCallback = - std::function, SHAMapHash const&)>; + using descendCallback = std::function; SHAMapTreeNode* descendAsync( SHAMapInnerNode* parent, @@ -433,7 +424,7 @@ private: // Non-storing // Does not hook the returned node to its parent - intr_ptr::SharedPtr + SHAMapTreeNodePtr descendNoStore(SHAMapInnerNode&, int branch) const; /** If there is only one leaf below this node, get its contents */ @@ -495,10 +486,10 @@ private: // nodes we may have acquired from deferred reads using DeferredNode = std::tuple< - SHAMapInnerNode*, // parent node - SHAMapNodeID, // parent node ID - int, // branch - intr_ptr::SharedPtr>; // node + SHAMapInnerNode*, // parent node + SHAMapNodeID, // parent node ID + int, // branch + SHAMapTreeNodePtr>; // node int deferred; std::mutex deferLock; @@ -524,7 +515,7 @@ private: gmnProcessDeferredReads(MissingNodes&); // fetch from DB helper function - intr_ptr::SharedPtr + SHAMapTreeNodePtr finishFetch(SHAMapHash const& hash, std::shared_ptr const& object) const; }; diff --git a/include/xrpl/shamap/SHAMapAccountStateLeafNode.h b/include/xrpl/shamap/SHAMapAccountStateLeafNode.h index c67b32d4e7..e388d205d1 100644 --- a/include/xrpl/shamap/SHAMapAccountStateLeafNode.h +++ b/include/xrpl/shamap/SHAMapAccountStateLeafNode.h @@ -27,7 +27,7 @@ public: { } - intr_ptr::SharedPtr + SHAMapTreeNodePtr clone(std::uint32_t cowid) const final { return intr_ptr::makeShared(item_, cowid, hash_); diff --git a/include/xrpl/shamap/SHAMapInnerNode.h b/include/xrpl/shamap/SHAMapInnerNode.h index 48416a93e6..cafb498218 100644 --- a/include/xrpl/shamap/SHAMapInnerNode.h +++ b/include/xrpl/shamap/SHAMapInnerNode.h @@ -87,7 +87,7 @@ public: void partialDestructor() override; - intr_ptr::SharedPtr + SHAMapTreeNodePtr clone(std::uint32_t cowid) const override; SHAMapNodeType @@ -121,19 +121,19 @@ public: getChildHash(int m) const; void - setChild(int m, intr_ptr::SharedPtr child); + setChild(int m, SHAMapTreeNodePtr child); void - shareChild(int m, intr_ptr::SharedPtr const& child); + shareChild(int m, SHAMapTreeNodePtr const& child); SHAMapTreeNode* getChildPointer(int branch); - intr_ptr::SharedPtr + SHAMapTreeNodePtr getChild(int branch); - intr_ptr::SharedPtr - canonicalizeChild(int branch, intr_ptr::SharedPtr node); + SHAMapTreeNodePtr + canonicalizeChild(int branch, SHAMapTreeNodePtr node); // sync functions bool @@ -161,10 +161,10 @@ public: void invariants(bool isRoot = false) const override; - static intr_ptr::SharedPtr + static SHAMapTreeNodePtr makeFullInner(Slice data, SHAMapHash const& hash, bool hashValid); - static intr_ptr::SharedPtr + static SHAMapTreeNodePtr makeCompressedInner(Slice data); }; diff --git a/include/xrpl/shamap/SHAMapTreeNode.h b/include/xrpl/shamap/SHAMapTreeNode.h index ee74155ac4..5cca2ea41a 100644 --- a/include/xrpl/shamap/SHAMapTreeNode.h +++ b/include/xrpl/shamap/SHAMapTreeNode.h @@ -13,6 +13,9 @@ namespace xrpl { +class SHAMapTreeNode; +using SHAMapTreeNodePtr = intr_ptr::SharedPtr; + // These are wire-protocol identifiers used during serialization to encode the // type of a node. They should not be arbitrarily be changed. static constexpr unsigned char const kWireTypeTransaction = 0; @@ -112,7 +115,7 @@ public: } /** Make a copy of this node, setting the owner. */ - virtual intr_ptr::SharedPtr + virtual SHAMapTreeNodePtr clone(std::uint32_t cowid) const = 0; /** @} */ @@ -153,20 +156,20 @@ public: virtual void invariants(bool isRoot = false) const = 0; - static intr_ptr::SharedPtr + static SHAMapTreeNodePtr makeFromPrefix(Slice rawNode, SHAMapHash const& hash); - static intr_ptr::SharedPtr + static SHAMapTreeNodePtr makeFromWire(Slice rawNode); private: - static intr_ptr::SharedPtr + static SHAMapTreeNodePtr makeTransaction(Slice data, SHAMapHash const& hash, bool hashValid); - static intr_ptr::SharedPtr + static SHAMapTreeNodePtr makeAccountState(Slice data, SHAMapHash const& hash, bool hashValid); - static intr_ptr::SharedPtr + static SHAMapTreeNodePtr makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool hashValid); }; diff --git a/include/xrpl/shamap/SHAMapTxLeafNode.h b/include/xrpl/shamap/SHAMapTxLeafNode.h index 72be5b1962..49f4f90906 100644 --- a/include/xrpl/shamap/SHAMapTxLeafNode.h +++ b/include/xrpl/shamap/SHAMapTxLeafNode.h @@ -26,7 +26,7 @@ public: { } - intr_ptr::SharedPtr + SHAMapTreeNodePtr clone(std::uint32_t cowid) const final { return intr_ptr::makeShared(item_, cowid, hash_); diff --git a/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h b/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h index 44562aeaba..3f4163ac41 100644 --- a/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h +++ b/include/xrpl/shamap/SHAMapTxPlusMetaLeafNode.h @@ -27,7 +27,7 @@ public: { } - intr_ptr::SharedPtr + SHAMapTreeNodePtr clone(std::uint32_t cowid) const override { return intr_ptr::makeShared(item_, cowid, hash_); diff --git a/include/xrpl/shamap/TreeNodeCache.h b/include/xrpl/shamap/TreeNodeCache.h index 4edb6348ec..2d5782c7e9 100644 --- a/include/xrpl/shamap/TreeNodeCache.h +++ b/include/xrpl/shamap/TreeNodeCache.h @@ -11,5 +11,5 @@ using TreeNodeCache = TaggedCache< SHAMapTreeNode, /*IsKeyCache*/ false, intr_ptr::SharedWeakUnionPtr, - intr_ptr::SharedPtr>; + SHAMapTreeNodePtr>; } // namespace xrpl diff --git a/include/xrpl/shamap/detail/TaggedPointer.h b/include/xrpl/shamap/detail/TaggedPointer.h index 94dbe95284..5eb3863de0 100644 --- a/include/xrpl/shamap/detail/TaggedPointer.h +++ b/include/xrpl/shamap/detail/TaggedPointer.h @@ -148,7 +148,7 @@ public: /** Get the number of elements in each array and a pointer to the start of each array. */ - [[nodiscard]] std::tuple*> + [[nodiscard]] std::tuple getHashesAndChildren() const; /** Get the `hashes` array */ @@ -156,7 +156,7 @@ public: getHashes() const; /** Get the `children` array */ - [[nodiscard]] intr_ptr::SharedPtr* + [[nodiscard]] SHAMapTreeNodePtr* getChildren() const; /** Call the `f` callback for all 16 (branchFactor) branches - even if diff --git a/include/xrpl/shamap/detail/TaggedPointer.ipp b/include/xrpl/shamap/detail/TaggedPointer.ipp index 2e6e31fed8..6606c49a6b 100644 --- a/include/xrpl/shamap/detail/TaggedPointer.ipp +++ b/include/xrpl/shamap/detail/TaggedPointer.ipp @@ -26,8 +26,7 @@ static_assert( // Terminology: A chunk is the memory being allocated from a block. A block // contains multiple chunks. This is the terminology the boost documentation // uses. Pools use "Simple Segregated Storage" as their storage format. -constexpr size_t kElementSizeBytes = - (sizeof(SHAMapHash) + sizeof(intr_ptr::SharedPtr)); +constexpr size_t kElementSizeBytes = sizeof(SHAMapHash) + sizeof(SHAMapTreeNodePtr); constexpr size_t kBlockSizeBytes = kilobytes(512); @@ -364,8 +363,7 @@ inline TaggedPointer::TaggedPointer( // keep new (&dstHashes[dstIndex]) SHAMapHash{srcHashes[srcIndex]}; - new (&dstChildren[dstIndex]) - intr_ptr::SharedPtr{std::move(srcChildren[srcIndex])}; + new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{std::move(srcChildren[srcIndex])}; ++dstIndex; ++srcIndex; } @@ -376,7 +374,7 @@ inline TaggedPointer::TaggedPointer( if (dstIsDense) { new (&dstHashes[dstIndex]) SHAMapHash{}; - new (&dstChildren[dstIndex]) intr_ptr::SharedPtr{}; + new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{}; ++dstIndex; } } @@ -384,7 +382,7 @@ inline TaggedPointer::TaggedPointer( { // add new (&dstHashes[dstIndex]) SHAMapHash{}; - new (&dstChildren[dstIndex]) intr_ptr::SharedPtr{}; + new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{}; ++dstIndex; if (srcIsDense) { @@ -397,7 +395,7 @@ inline TaggedPointer::TaggedPointer( if (dstIsDense) { new (&dstHashes[dstIndex]) SHAMapHash{}; - new (&dstChildren[dstIndex]) intr_ptr::SharedPtr{}; + new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{}; ++dstIndex; } if (srcIsDense) @@ -414,7 +412,7 @@ inline TaggedPointer::TaggedPointer( for (int i = dstIndex; i < dstNumAllocated; ++i) { new (&dstHashes[i]) SHAMapHash{}; - new (&dstChildren[i]) intr_ptr::SharedPtr{}; + new (&dstChildren[i]) SHAMapTreeNodePtr{}; } *this = std::move(dst); } @@ -433,8 +431,10 @@ inline TaggedPointer::TaggedPointer( // allocate hashes and children, but do not run constructors TaggedPointer newHashesAndChildren{RawAllocateTag{}, toAllocate}; - SHAMapHash *newHashes = nullptr, *oldHashes = nullptr; - intr_ptr::SharedPtr*newChildren = nullptr, *oldChildren = nullptr; + SHAMapHash* newHashes = nullptr; + SHAMapHash* oldHashes = nullptr; + SHAMapTreeNodePtr* newChildren = nullptr; + SHAMapTreeNodePtr* oldChildren = nullptr; std::uint8_t newNumAllocated = 0; // structured bindings can't be captured in c++ 17; use tie instead std::tie(newNumAllocated, newHashes, newChildren) = newHashesAndChildren.getHashesAndChildren(); @@ -445,8 +445,7 @@ inline TaggedPointer::TaggedPointer( // new arrays are dense, old arrays are sparse iterNonEmptyChildIndexes(isBranch, [&](auto branchNum, auto indexNum) { new (&newHashes[branchNum]) SHAMapHash{oldHashes[indexNum]}; - new (&newChildren[branchNum]) - intr_ptr::SharedPtr{std::move(oldChildren[indexNum])}; + new (&newChildren[branchNum]) SHAMapTreeNodePtr{std::move(oldChildren[indexNum])}; }); // Run the constructors for the remaining elements for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i) @@ -454,7 +453,7 @@ inline TaggedPointer::TaggedPointer( if (((1 << i) & isBranch) != 0) continue; new (&newHashes[i]) SHAMapHash{}; - new (&newChildren[i]) intr_ptr::SharedPtr{}; + new (&newChildren[i]) SHAMapTreeNodePtr{}; } } else @@ -464,14 +463,14 @@ inline TaggedPointer::TaggedPointer( iterNonEmptyChildIndexes(isBranch, [&](auto branchNum, auto indexNum) { new (&newHashes[curCompressedIndex]) SHAMapHash{oldHashes[indexNum]}; new (&newChildren[curCompressedIndex]) - intr_ptr::SharedPtr{std::move(oldChildren[indexNum])}; + SHAMapTreeNodePtr{std::move(oldChildren[indexNum])}; ++curCompressedIndex; }); // Run the constructors for the remaining elements for (int i = curCompressedIndex; i < newNumAllocated; ++i) { new (&newHashes[i]) SHAMapHash{}; - new (&newChildren[i]) intr_ptr::SharedPtr{}; + new (&newChildren[i]) SHAMapTreeNodePtr{}; } } @@ -485,7 +484,7 @@ inline TaggedPointer::TaggedPointer(std::uint8_t numChildren) for (std::size_t i = 0; i < numAllocated; ++i) { new (&hashes[i]) SHAMapHash{}; - new (&children[i]) intr_ptr::SharedPtr{}; + new (&children[i]) SHAMapTreeNodePtr{}; } } @@ -523,14 +522,13 @@ TaggedPointer::isDense() const return (tp_ & kTagMask) == kBoundaries.size() - 1; } -[[nodiscard]] inline std::tuple*> +[[nodiscard]] inline std::tuple TaggedPointer::getHashesAndChildren() const { auto const [tag, ptr] = decode(); auto const hashes = reinterpret_cast(ptr); std::uint8_t const numAllocated = kBoundaries[tag]; - auto const children = - reinterpret_cast*>(hashes + numAllocated); + auto const children = reinterpret_cast(hashes + numAllocated); return {numAllocated, hashes, children}; }; @@ -540,7 +538,7 @@ TaggedPointer::getHashes() const return reinterpret_cast(tp_ & kPtrMask); }; -[[nodiscard]] inline intr_ptr::SharedPtr* +[[nodiscard]] inline SHAMapTreeNodePtr* TaggedPointer::getChildren() const { auto [unused1, unused2, result] = getHashesAndChildren(); diff --git a/src/libxrpl/shamap/SHAMap.cpp b/src/libxrpl/shamap/SHAMap.cpp index d3a7d49da6..4aad255d81 100644 --- a/src/libxrpl/shamap/SHAMap.cpp +++ b/src/libxrpl/shamap/SHAMap.cpp @@ -97,10 +97,7 @@ SHAMap::snapShot(bool isMutable) const } void -SHAMap::dirtyUp( - SharedPtrNodeStack& stack, - uint256 const& target, - intr_ptr::SharedPtr child) +SHAMap::dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, SHAMapTreeNodePtr child) { // walk the tree up from through the inner nodes to the root_ // update hashes and links @@ -165,7 +162,7 @@ SHAMap::findKey(uint256 const& id) const return leaf; } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const { XRPL_ASSERT(backed_, "xrpl::SHAMap::fetchNodeFromDB : is backed"); @@ -173,7 +170,7 @@ SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const return finishFetch(hash, obj); } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::finishFetch(SHAMapHash const& hash, std::shared_ptr const& object) const { XRPL_ASSERT(backed_, "xrpl::SHAMap::finishFetch : is backed"); @@ -208,7 +205,7 @@ SHAMap::finishFetch(SHAMapHash const& hash, std::shared_ptr const& o } // See if a sync filter has a node -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const { if (auto nodeData = filter->getNode(hash)) @@ -234,7 +231,7 @@ SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const // Get a node without throwing // Used on maps where missing nodes are expected -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const { auto node = cacheLookup(hash); @@ -257,7 +254,7 @@ SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const return node; } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::fetchNodeNT(SHAMapHash const& hash) const { auto node = cacheLookup(hash); @@ -269,7 +266,7 @@ SHAMap::fetchNodeNT(SHAMapHash const& hash) const } // Throw if the node is missing -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::fetchNode(SHAMapHash const& hash) const { auto node = fetchNodeNT(hash); @@ -291,10 +288,10 @@ SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const return ret; } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::descendThrow(SHAMapInnerNode& parent, int branch) const { - intr_ptr::SharedPtr ret = descend(parent, branch); + SHAMapTreeNodePtr ret = descend(parent, branch); if (!ret && !parent.isEmptyBranch(branch)) Throw(type_, parent.getChildHash(branch)); @@ -309,7 +306,7 @@ SHAMap::descend(SHAMapInnerNode* parent, int branch) const if ((ret != nullptr) || !backed_) return ret; - intr_ptr::SharedPtr node = fetchNodeNT(parent->getChildHash(branch)); + SHAMapTreeNodePtr node = fetchNodeNT(parent->getChildHash(branch)); if (!node) return nullptr; @@ -317,10 +314,10 @@ SHAMap::descend(SHAMapInnerNode* parent, int branch) const return node.get(); } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::descend(SHAMapInnerNode& parent, int branch) const { - intr_ptr::SharedPtr node = parent.getChild(branch); + SHAMapTreeNodePtr node = parent.getChild(branch); if (node || !backed_) return node; @@ -334,10 +331,10 @@ SHAMap::descend(SHAMapInnerNode& parent, int branch) const // Gets the node that would be hooked to this branch, // but doesn't hook it up. -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::descendNoStore(SHAMapInnerNode& parent, int branch) const { - intr_ptr::SharedPtr ret = parent.getChild(branch); + SHAMapTreeNodePtr ret = parent.getChild(branch); if (!ret && backed_) ret = fetchNode(parent.getChildHash(branch)); return ret; @@ -361,7 +358,7 @@ SHAMap::descend( if (child == nullptr) { auto const& childHash = parent->getChildHash(branch); - intr_ptr::SharedPtr childNode = fetchNodeNT(childHash, filter); + SHAMapTreeNodePtr childNode = fetchNodeNT(childHash, filter); if (childNode) { @@ -434,7 +431,7 @@ SHAMap::unshareNode(intr_ptr::SharedPtr node, SHAMapNodeID const& nodeID) SHAMapLeafNode* SHAMap::belowHelper( - intr_ptr::SharedPtr node, + SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch, std::tuple, std::function> const& loopParams) const @@ -479,8 +476,7 @@ SHAMap::belowHelper( return nullptr; } SHAMapLeafNode* -SHAMap::lastBelow(intr_ptr::SharedPtr node, SharedPtrNodeStack& stack, int branch) - const +SHAMap::lastBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch) const { auto init = kBranchFactor - 1; auto cmp = [](int i) { return i >= 0; }; @@ -489,8 +485,7 @@ SHAMap::lastBelow(intr_ptr::SharedPtr node, SharedPtrNodeStack& return belowHelper(node, stack, branch, {init, cmp, incr}); } SHAMapLeafNode* -SHAMap::firstBelow(intr_ptr::SharedPtr node, SharedPtrNodeStack& stack, int branch) - const +SHAMap::firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch) const { auto init = 0; auto cmp = [](int i) { return i <= kBranchFactor; }; @@ -699,10 +694,8 @@ SHAMap::delItem(uint256 const& id) SHAMapNodeType const type = leaf->getType(); - using TreeNodeType = intr_ptr::SharedPtr; - // What gets attached to the end of the chain (For now, nothing, since we deleted the leaf) - TreeNodeType prevNode; + SHAMapTreeNodePtr prevNode; while (!stack.empty()) { @@ -728,7 +721,7 @@ SHAMap::delItem(uint256 const& id) // no children below this branch // // Note: This is unnecessary due to the std::move above but left here for safety - prevNode = TreeNodeType{}; + prevNode = SHAMapTreeNodePtr{}; } else if (bc == 1) { @@ -741,7 +734,7 @@ SHAMap::delItem(uint256 const& id) { if (!node->isEmptyBranch(i)) { - node->setChild(i, TreeNodeType{}); + node->setChild(i, SHAMapTreeNodePtr{}); break; } } @@ -937,8 +930,8 @@ SHAMap::fetchRoot(SHAMapHash const& hash, SHAMapSyncFilter* filter) @note The node must have already been unshared by having the caller first call SHAMapTreeNode::unshare(). */ -intr_ptr::SharedPtr -SHAMap::writeNode(NodeObjectType t, intr_ptr::SharedPtr node) const +SHAMapTreeNodePtr +SHAMap::writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const { XRPL_ASSERT(node->cowid() == 0, "xrpl::SHAMap::writeNode : valid input node"); XRPL_ASSERT(backed_, "xrpl::SHAMap::writeNode : is backed"); @@ -1155,7 +1148,7 @@ SHAMap::dump(bool hash) const JLOG(journal_.info()) << leafCount << " resident leaves"; } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMap::cacheLookup(SHAMapHash const& hash) const { auto ret = f_.getTreeNodeCache()->fetch(hash.asUInt256()); @@ -1164,7 +1157,7 @@ SHAMap::cacheLookup(SHAMapHash const& hash) const } void -SHAMap::canonicalize(SHAMapHash const& hash, intr_ptr::SharedPtr& node) const +SHAMap::canonicalize(SHAMapHash const& hash, SHAMapTreeNodePtr& node) const { XRPL_ASSERT(backed_, "xrpl::SHAMap::canonicalize : is backed"); XRPL_ASSERT(node->cowid() == 0, "xrpl::SHAMap::canonicalize : valid node input"); diff --git a/src/libxrpl/shamap/SHAMapDelta.cpp b/src/libxrpl/shamap/SHAMapDelta.cpp index b1aeac18e8..8336ce5481 100644 --- a/src/libxrpl/shamap/SHAMapDelta.cpp +++ b/src/libxrpl/shamap/SHAMapDelta.cpp @@ -261,7 +261,7 @@ SHAMap::walkMap(std::vector& missingNodes, int maxMissing) co { if (!node->isEmptyBranch(i)) { - intr_ptr::SharedPtr const nextNode = descendNoStore(*node, i); + SHAMapTreeNodePtr const nextNode = descendNoStore(*node, i); if (nextNode) { @@ -286,7 +286,7 @@ SHAMap::walkMapParallel(std::vector& missingNodes, int maxMis return false; using StackEntry = intr_ptr::SharedPtr; - std::array, 16> topChildren; + std::array topChildren; { auto const& innerRoot = intr_ptr::staticPointerCast(root_); for (int i = 0; i < 16; ++i) @@ -331,8 +331,7 @@ SHAMap::walkMapParallel(std::vector& missingNodes, int maxMis { if (node->isEmptyBranch(i)) continue; - intr_ptr::SharedPtr const nextNode = - descendNoStore(*node, i); + SHAMapTreeNodePtr const nextNode = descendNoStore(*node, i); if (nextNode) { diff --git a/src/libxrpl/shamap/SHAMapInnerNode.cpp b/src/libxrpl/shamap/SHAMapInnerNode.cpp index f31b75ad39..ee6ebf7f3f 100644 --- a/src/libxrpl/shamap/SHAMapInnerNode.cpp +++ b/src/libxrpl/shamap/SHAMapInnerNode.cpp @@ -37,7 +37,7 @@ SHAMapInnerNode::~SHAMapInnerNode() = default; void SHAMapInnerNode::partialDestructor() { - intr_ptr::SharedPtr* children = nullptr; + SHAMapTreeNodePtr* children = nullptr; // structured bindings can't be captured in c++ 17; use tie instead std::tie(std::ignore, std::ignore, children) = hashesAndChildren_.getHashesAndChildren(); iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { children[indexNum].reset(); }); @@ -69,7 +69,7 @@ SHAMapInnerNode::getChildIndex(int i) const return hashesAndChildren_.getChildIndex(isBranch_, i); } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMapInnerNode::clone(std::uint32_t cowid) const { auto const branchCount = getBranchCount(); @@ -78,8 +78,10 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const p->hash_ = hash_; p->isBranch_ = isBranch_; p->fullBelowGen_ = fullBelowGen_; - SHAMapHash *cloneHashes = nullptr, *thisHashes = nullptr; - intr_ptr::SharedPtr*cloneChildren = nullptr, *thisChildren = nullptr; + SHAMapHash* cloneHashes = nullptr; + SHAMapHash* thisHashes = nullptr; + SHAMapTreeNodePtr* cloneChildren = nullptr; + SHAMapTreeNodePtr* thisChildren = nullptr; // structured bindings can't be captured in c++ 17; use tie instead std::tie(std::ignore, cloneHashes, cloneChildren) = p->hashesAndChildren_.getHashesAndChildren(); @@ -118,7 +120,7 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const return p; } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMapInnerNode::makeFullInner(Slice data, SHAMapHash const& hash, bool hashValid) { // A full inner node is serialized as 16 256-bit hashes, back to back: @@ -153,7 +155,7 @@ SHAMapInnerNode::makeFullInner(Slice data, SHAMapHash const& hash, bool hashVali return ret; } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMapInnerNode::makeCompressedInner(Slice data) { // A compressed inner node is serialized as a series of 33 byte chunks, @@ -207,7 +209,7 @@ void SHAMapInnerNode::updateHashDeep() { SHAMapHash* hashes = nullptr; - intr_ptr::SharedPtr* children = nullptr; + SHAMapTreeNodePtr* children = nullptr; // structured bindings can't be captured in c++ 17; use tie instead std::tie(std::ignore, hashes, children) = hashesAndChildren_.getHashesAndChildren(); iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { @@ -265,7 +267,7 @@ SHAMapInnerNode::getString(SHAMapNodeID const& id) const // We are modifying an inner node void -SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr child) +SHAMapInnerNode::setChild(int m, SHAMapTreeNodePtr child) { XRPL_ASSERT( (m >= 0) && (m < kBranchFactor), "xrpl::SHAMapInnerNode::setChild : valid branch input"); @@ -307,7 +309,7 @@ SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr child) // finished modifying, now make shareable void -SHAMapInnerNode::shareChild(int m, intr_ptr::SharedPtr const& child) +SHAMapInnerNode::shareChild(int m, SHAMapTreeNodePtr const& child) { XRPL_ASSERT( (m >= 0) && (m < kBranchFactor), "xrpl::SHAMapInnerNode::shareChild : valid branch input"); @@ -337,7 +339,7 @@ SHAMapInnerNode::getChildPointer(int branch) return hashesAndChildren_.getChildren()[index].get(); } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMapInnerNode::getChild(int branch) { XRPL_ASSERT( @@ -365,8 +367,8 @@ SHAMapInnerNode::getChildHash(int m) const return kZeroShaMapHash; } -intr_ptr::SharedPtr -SHAMapInnerNode::canonicalizeChild(int branch, intr_ptr::SharedPtr node) +SHAMapTreeNodePtr +SHAMapInnerNode::canonicalizeChild(int branch, SHAMapTreeNodePtr node) { XRPL_ASSERT( branch >= 0 && branch < kBranchFactor, diff --git a/src/libxrpl/shamap/SHAMapSync.cpp b/src/libxrpl/shamap/SHAMapSync.cpp index cd2654c603..0601bfefda 100644 --- a/src/libxrpl/shamap/SHAMapSync.cpp +++ b/src/libxrpl/shamap/SHAMapSync.cpp @@ -66,7 +66,7 @@ SHAMap::visitNodes(std::function const& function) const { if (!node->isEmptyBranch(pos)) { - intr_ptr::SharedPtr const child = descendNoStore(*node, pos); + SHAMapTreeNodePtr const child = descendNoStore(*node, pos); if (!function(*child)) return; @@ -204,8 +204,7 @@ SHAMap::gmnProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se) branch, mn.filter, pending, - [node, nodeID, branch, &mn]( - intr_ptr::SharedPtr found, SHAMapHash const&) { + [node, nodeID, branch, &mn](SHAMapTreeNodePtr found, SHAMapHash const&) { // a read completed asynchronously std::unique_lock const lock{mn.deferLock}; mn.finishedReads.emplace_back(node, nodeID, branch, std::move(found)); @@ -266,8 +265,7 @@ SHAMap::gmnProcessDeferredReads(MissingNodes& mn) int complete = 0; while (complete != mn.deferred) { - std::tuple> - deferredNode; + std::tuple deferredNode; { std::unique_lock lock{mn.deferLock}; diff --git a/src/libxrpl/shamap/SHAMapTreeNode.cpp b/src/libxrpl/shamap/SHAMapTreeNode.cpp index 3b8d976c69..1ae7cf18af 100644 --- a/src/libxrpl/shamap/SHAMapTreeNode.cpp +++ b/src/libxrpl/shamap/SHAMapTreeNode.cpp @@ -25,7 +25,7 @@ namespace xrpl { -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMapTreeNode::makeTransaction(Slice data, SHAMapHash const& hash, bool hashValid) { if (data.size() < kMinShaMapItemBytes) @@ -43,7 +43,7 @@ SHAMapTreeNode::makeTransaction(Slice data, SHAMapHash const& hash, bool hashVal return intr_ptr::makeShared(std::move(item), 0); } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMapTreeNode::makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool hashValid) { Serializer s(data.data(), data.size()); @@ -83,7 +83,7 @@ SHAMapTreeNode::makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool return intr_ptr::makeShared(std::move(item), 0); } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMapTreeNode::makeAccountState(Slice data, SHAMapHash const& hash, bool hashValid) { Serializer s(data.data(), data.size()); @@ -124,7 +124,7 @@ SHAMapTreeNode::makeAccountState(Slice data, SHAMapHash const& hash, bool hashVa return intr_ptr::makeShared(std::move(item), 0); } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMapTreeNode::makeFromWire(Slice rawNode) { if (rawNode.empty()) @@ -155,7 +155,7 @@ SHAMapTreeNode::makeFromWire(Slice rawNode) Throw("wire: Unknown type (" + std::to_string(type) + ")"); } -intr_ptr::SharedPtr +SHAMapTreeNodePtr SHAMapTreeNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash) { if (rawNode.size() < 4) From 023bdaeeedc6ebcff3a2eef136d0f2bdac4a8296 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 3 Jun 2026 20:14:17 +0100 Subject: [PATCH 57/87] ci: Install gcov, nettools, cacert in nix images (#7398) --- docker/{check-tool-versions.sh => check-tools.sh} | 8 ++++++++ docker/nix.Dockerfile | 10 ++++++++-- nix/ci-env.nix | 14 ++++++++++++++ nix/packages.nix | 1 + 4 files changed, 31 insertions(+), 2 deletions(-) rename docker/{check-tool-versions.sh => check-tools.sh} (59%) diff --git a/docker/check-tool-versions.sh b/docker/check-tools.sh similarity index 59% rename from docker/check-tool-versions.sh rename to docker/check-tools.sh index db20be45e4..faa4586832 100755 --- a/docker/check-tool-versions.sh +++ b/docker/check-tools.sh @@ -10,11 +10,13 @@ cmake --version conan --version g++ --version gcc --version +gcov --version gcovr --version git --version less --version make --version mold --version +netstat --version ninja --version perl --version pkg-config --version @@ -22,3 +24,9 @@ pre-commit --version python3 --version run-clang-tidy --help vim --version + +# A simple test to verify that git can clone a repository over HTTPS +# (i.e. the CA bundle is wired up). Clone to a temp dir and clean up. +tmp_clone="$(mktemp -d)" +git clone --depth 1 https://github.com/XRPLF/actions.git "${tmp_clone}/actions" +rm -rf "${tmp_clone}" diff --git a/docker/nix.Dockerfile b/docker/nix.Dockerfile index a0eab31769..6248708417 100644 --- a/docker/nix.Dockerfile +++ b/docker/nix.Dockerfile @@ -47,6 +47,12 @@ COPY --from=builder /tmp/build/result /nix/ci-env ENV PATH="/nix/ci-env/bin:${PATH}" +# Point HTTPS clients (git, curl, conan, ...) at the CA bundle shipped in the +# Nix CI environment, so TLS verification works without ca-certificates being +# installed in the system. +ENV SSL_CERT_FILE="/nix/ci-env/etc/ssl/certs/ca-bundle.crt" +ENV GIT_SSL_CAINFO="/nix/ci-env/etc/ssl/certs/ca-bundle.crt" + # Externally-built dynamically-linked ELF binaries hard-code the loader path # (e.g. /lib64/ld-linux-x86-64.so.2) in their PT_INTERP header. Install it # from the Nix store when the base image doesn't already provide one. @@ -65,8 +71,8 @@ if [ ! -e "${target}" ]; then fi EOF -COPY docker/check-tool-versions.sh /tmp/check-tool-versions.sh -RUN /tmp/check-tool-versions.sh +COPY docker/check-tools.sh /tmp/check-tools.sh +RUN /tmp/check-tools.sh # Sanity-check that the g++/clang++ are able to build binaries, including sanitizer-instrumented ones. COPY docker/test_files/cpp_sources/ /tmp/cpp_sources/ diff --git a/nix/ci-env.nix b/nix/ci-env.nix index 0d617913d9..0ef7410250 100644 --- a/nix/ci-env.nix +++ b/nix/ci-env.nix @@ -43,6 +43,15 @@ let bintools = customBinutils; }; + # gcov ships in gcc's `cc` output, but the cc-wrapper doesn't expose it. + # Surface the gcov from our rebuilt gcc (linked against the custom glibc, so + # it runs under the loader installed in the image) and matching the exact + # compiler version, so gcovr can produce coverage reports in the CI env. + customGcov = pkgs.runCommand "gcov-custom-for-ci-env" { } '' + mkdir -p "$out/bin" + ln -s "${customGccCc}/bin/gcov" "$out/bin/gcov" + ''; + # stdenv built around the rebuilt gcc / custom glibc. Used to rebuild # compiler-rt below so its sanitizer runtimes see the custom glibc # headers. @@ -105,11 +114,16 @@ in name = "xrpld-ci-env"; paths = commonPackages ++ [ customGcc + customGcov customClangForCiEnv customBinutils + # CA certificate bundle so HTTPS clients (git, curl, conan) can verify + # TLS connections without ca-certificates being installed in the system. + pkgs.cacert ]; pathsToLink = [ "/bin" + "/etc/ssl/certs" "/lib" "/include" "/share" diff --git a/nix/packages.nix b/nix/packages.nix index 3d92fedb4b..b608677aea 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -17,6 +17,7 @@ in llvmPackages_22.clang-tools less # needed for git diff mold + nettools # provides netstat, used to debug failures in CI ninja patchelf perl # needed for openssl From e5cf1a0985ee27e5519fa5de1d0a664c57b73d1a Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Wed, 3 Jun 2026 15:30:20 -0400 Subject: [PATCH 58/87] fix: Add zero NFT Offer ID check for NFTokenCancelOffer (#7391) --- .../tx/transactors/nft/NFTokenCancelOffer.cpp | 11 +++++++++-- src/test/app/NFToken_test.cpp | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp index 1614f90202..d04714907e 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenCancelOffer.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -21,8 +22,14 @@ namespace xrpl { NotTEC NFTokenCancelOffer::preflight(PreflightContext const& ctx) { - if (auto const& ids = ctx.tx[sfNFTokenOffers]; - ids.empty() || (ids.size() > kMaxTokenOfferCancelCount)) + auto const& offerIds = ctx.tx[sfNFTokenOffers]; + + if (offerIds.empty() || (offerIds.size() > kMaxTokenOfferCancelCount)) + return temMALFORMED; + + // Zero offer IDs cannot be passed as ledger entry keys. + if (ctx.rules.enabled(fixCleanup3_2_0) && + std::ranges::any_of(offerIds, [](uint256 const& id) { return id.isZero(); })) return temMALFORMED; // In order to prevent unnecessarily overlarge transactions, we diff --git a/src/test/app/NFToken_test.cpp b/src/test/app/NFToken_test.cpp index ebd470ec92..269bc72c53 100644 --- a/src/test/app/NFToken_test.cpp +++ b/src/test/app/NFToken_test.cpp @@ -892,6 +892,25 @@ class NFTokenBaseUtil_test : public beast::unit_test::Suite BEAST_EXPECT(ownerCount(env, buyer) == 1); } + // Only test this with fixCleanup3_2_0 enabled. Without the fix, + // an assert-enabled build can crash when Ledger::read() receives + // a zero-key offer ID. + if (features[fixCleanup3_2_0]) + { + // Zero is not a valid offer ID. + env(token::cancelOffer(buyer, {uint256{}}), Ter(temMALFORMED)); + env.close(); + BEAST_EXPECT(ownerCount(env, buyer) == 1); + + // List of offer IDs containing zero is invalid. + // craftedIndex is not a valid offer index but it is not zero. + auto const craftedIndex = keylet::nftoffer(gw, env.seq(gw)).key; + env(token::cancelOffer(buyer, {buyerOfferIndex, uint256{}, craftedIndex}), + Ter(temMALFORMED)); + env.close(); + BEAST_EXPECT(ownerCount(env, buyer) == 1); + } + // List of tokens to delete is too long. { std::vector const offers(kMaxTokenOfferCancelCount + 1, buyerOfferIndex); From 6c543426c3461f98d62146a445dada2f47516a93 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 3 Jun 2026 23:19:15 +0100 Subject: [PATCH 59/87] ci: Fix clang asan include dirs in nix images, add curl & gnupg (#7400) --- cspell.config.yaml | 1 + docker/check-tools.sh | 2 ++ docker/test_files/cpp_sources/asan.cpp | 7 +++++++ nix/ci-env.nix | 8 ++++++++ nix/packages.nix | 2 ++ 5 files changed, 20 insertions(+) diff --git a/cspell.config.yaml b/cspell.config.yaml index ed936941e4..da5dc9b072 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -134,6 +134,7 @@ words: - iou - ious - isrdc + - isystem - itype - jemalloc - jlog diff --git a/docker/check-tools.sh b/docker/check-tools.sh index faa4586832..eb72e8f357 100755 --- a/docker/check-tools.sh +++ b/docker/check-tools.sh @@ -8,11 +8,13 @@ clang++ --version clang-format --version cmake --version conan --version +curl --version g++ --version gcc --version gcov --version gcovr --version git --version +gpg --version less --version make --version mold --version diff --git a/docker/test_files/cpp_sources/asan.cpp b/docker/test_files/cpp_sources/asan.cpp index 8347f58d37..deefdec79a 100644 --- a/docker/test_files/cpp_sources/asan.cpp +++ b/docker/test_files/cpp_sources/asan.cpp @@ -2,6 +2,13 @@ #include #include +// Regression test: the compiler-rt sanitizer interface headers must be on the +// include path. A bare on-PATH clang in the Nix CI env doesn't get them +// propagated automatically, so this include would fail to compile with clang++ +// if the env isn't wired up correctly. abseil hits the same include during +// sanitizer builds. LeakSanitizer ships with AddressSanitizer. +#include + #if defined(__clang__) || defined(__GNUC__) __attribute__((noinline)) #elif defined(_MSC_VER) diff --git a/nix/ci-env.nix b/nix/ci-env.nix index 0ef7410250..f823f71de0 100644 --- a/nix/ci-env.nix +++ b/nix/ci-env.nix @@ -94,6 +94,14 @@ let ln -s "${customCompilerRt.out}/lib" "$rsrc/lib" ln -s "${customCompilerRt.out}/share" "$rsrc/share" || true echo "-resource-dir=$rsrc" >> $out/nix-support/cc-cflags + # compiler-rt ships the sanitizer/profile/xray interface headers (e.g. + # ) in its `dev` output. In a normal Nix + # build these reach the include path because compiler-rt is propagated + # via depsTargetTargetPropagated and stdenv's setup hooks add its + # dev/include. The CI image runs clang outside a Nix stdenv (binaries + # on PATH, no setup hooks), so that never happens; add the headers + # explicitly. gcc ships its own copy, which is why this is clang-only. + echo "-isystem ${customCompilerRt.dev}/include" >> $out/nix-support/cc-cflags ''; }; diff --git a/nix/packages.nix b/nix/packages.nix index b608677aea..f282c15df9 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -11,9 +11,11 @@ in ccache cmake conan + curlMinimal # needed for codecov/codecov-action gcovr git gnumake + gnupg # needed for signing commits & codecov/codecov-action llvmPackages_22.clang-tools less # needed for git diff mold From 12e81abef3aa4c05bc2f21ea1be49644cb7d478a Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 4 Jun 2026 15:52:42 +0100 Subject: [PATCH 60/87] ci: Improve sanitizer-libs, add doxygen, dpkg, rpm in nix (#7403) --- docker/check-tools.sh | 3 + docker/install-sanitizer-libs.sh | 124 ++++++++++++++++++------------- nix/packages.nix | 3 + 3 files changed, 80 insertions(+), 50 deletions(-) diff --git a/docker/check-tools.sh b/docker/check-tools.sh index eb72e8f357..c446dc1b4a 100755 --- a/docker/check-tools.sh +++ b/docker/check-tools.sh @@ -9,6 +9,8 @@ clang-format --version cmake --version conan --version curl --version +doxygen --version +dpkg-buildpackage --version g++ --version gcc --version gcov --version @@ -24,6 +26,7 @@ perl --version pkg-config --version pre-commit --version python3 --version +rpmbuild --version run-clang-tidy --help vim --version diff --git a/docker/install-sanitizer-libs.sh b/docker/install-sanitizer-libs.sh index a28efeab3f..dc1ba1b350 100755 --- a/docker/install-sanitizer-libs.sh +++ b/docker/install-sanitizer-libs.sh @@ -27,63 +27,87 @@ fi echo "Detected OS: ${ID} ${VERSION_ID:-}" case "${ID}" in - debian) - apt-get update -y - apt-get install -y --no-install-recommends \ - libasan8 \ - libtsan2 \ - libubsan1 - - apt-get clean - rm -rf /var/lib/apt/lists/* + ubuntu | debian | rhel | centos | rocky | almalinux) + echo "Supported OS detected: ${ID}" ;; - - ubuntu) - apt-get update -y - apt-get install -y --no-install-recommends \ - gnupg \ - software-properties-common - add-apt-repository -y ppa:ubuntu-toolchain-r/test - apt-get update -y - apt-get install -y --no-install-recommends \ - libasan8 \ - libtsan2 \ - libubsan1 - - apt-get clean - rm -rf /var/lib/apt/lists/* - ;; - - rhel | centos | rocky | almalinux) - dnf install -y \ - libasan8 \ - libtsan2 \ - libubsan - - dnf clean -y all - rm -rf /var/cache/dnf/* - ;; - *) echo "ERROR: unsupported OS '${ID}'. Supported: debian, ubuntu, rhel-family" >&2 exit 1 ;; esac -# Verify that every expected library is now resolvable by the dynamic linker. -missing=0 -for lib in libasan.so.8 libtsan.so.2 libubsan.so.1; do - if ldconfig -p | grep -q "${lib}"; then - echo "OK: ${lib} found" - else - echo "ERROR: ${lib} not found after installation" >&2 - missing=$((missing + 1)) - fi -done +function preinstall() { + case "${ID}" in + ubuntu) + apt-get update -y + apt-get install -y --no-install-recommends \ + gnupg \ + software-properties-common + add-apt-repository -y ppa:ubuntu-toolchain-r/test + ;; + esac +} -if [ "${missing}" -ne 0 ]; then - echo "ERROR: ${missing} library/libraries missing" >&2 - exit 1 -fi +function install() { + case "${ID}" in + debian | ubuntu) + apt-get update -y + apt-get install -y --no-install-recommends \ + libasan8 \ + libtsan2 \ + libubsan1 + ;; + + rhel | centos | rocky | almalinux) + dnf install -y \ + libasan8 \ + libtsan2 \ + libubsan + ;; + esac +} + +function postinstall() { + # Don't clear cache in non-CI environments + if [ -z "${CI:-}" ]; then + echo "Not running in CI environment; skipping cache cleanup" + return + fi + + case "${ID}" in + debian | ubuntu) + apt-get clean + rm -rf /var/lib/apt/lists/* + ;; + + rhel | centos | rocky | almalinux) + dnf clean -y all + rm -rf /var/cache/dnf/* + ;; + esac +} + +function verify() { + # Verify that every expected library is now resolvable by the dynamic linker. + missing=0 + for lib in libasan.so.8 libtsan.so.2 libubsan.so.1; do + if ldconfig -p | grep -q "${lib}"; then + echo "OK: ${lib} found" + else + echo "ERROR: ${lib} not found after installation" >&2 + missing=$((missing + 1)) + fi + done + + if [ "${missing}" -ne 0 ]; then + echo "ERROR: ${missing} library/libraries missing" >&2 + exit 1 + fi +} + +preinstall +install +postinstall +verify echo "All sanitizer runtime libraries installed successfully." diff --git a/nix/packages.nix b/nix/packages.nix index f282c15df9..c51077367e 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -12,6 +12,8 @@ in cmake conan curlMinimal # needed for codecov/codecov-action + doxygen + dpkg # needed for dpkg-buildpackage gcovr git gnumake @@ -26,6 +28,7 @@ in pkg-config pre-commit python3 + rpm # needed for rpmbuild runClangTidy vim ]; From 5b8e6cd1dd6796c94cf471c223028d817cbc3906 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 4 Jun 2026 20:35:36 +0100 Subject: [PATCH 61/87] test: Fix LCOV_EXCL_END -> LCOV_EXCL_STOP (#7407) --- src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp index cfcf79fdba..05dcfea506 100644 --- a/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultWithdraw.cpp @@ -300,7 +300,7 @@ VaultWithdraw::doApply() << "VaultWithdraw: " // "Cannot burn all outstanding shares while unrealized loss is non-zero"; return tefINTERNAL; - // LCOV_EXCL_END + // LCOV_EXCL_STOP } STAmount const allAvailable{vaultAsset, *assetsAvailable}; From 8abe82eefa2357777af075d252ea8613f2948ae6 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 4 Jun 2026 21:02:59 +0100 Subject: [PATCH 62/87] ci: Redesign matrix configuration based on Nix images (#7385) Co-authored-by: semgrep-companion-app[bot] <218312740+semgrep-companion-app[bot]@users.noreply.github.com> --- .github/actions/build-deps/action.yml | 3 +- .github/actions/set-compiler-env/action.yml | 34 + .github/actions/setup-conan/action.yml | 25 +- .github/scripts/strategy-matrix/generate.py | 579 +++++++----------- .github/scripts/strategy-matrix/linux.json | 300 +++------ .github/scripts/strategy-matrix/macos.json | 24 +- .github/scripts/strategy-matrix/windows.json | 23 +- .github/workflows/on-tag.yml | 1 - .github/workflows/on-trigger.yml | 1 - .../workflows/reusable-build-test-config.yml | 49 +- .github/workflows/reusable-build-test.yml | 12 +- .github/workflows/reusable-package.yml | 39 +- .../workflows/reusable-strategy-matrix.yml | 15 +- .github/workflows/upload-conan-deps.yml | 11 +- cmake/XrplCompiler.cmake | 32 +- conan/profiles/ci | 7 + cspell.config.yaml | 1 + docker/check-tools.sh | 2 - nix/packages.nix | 2 - 19 files changed, 512 insertions(+), 648 deletions(-) create mode 100644 .github/actions/set-compiler-env/action.yml diff --git a/.github/actions/build-deps/action.yml b/.github/actions/build-deps/action.yml index 0891d56dfa..044c264ef0 100644 --- a/.github/actions/build-deps/action.yml +++ b/.github/actions/build-deps/action.yml @@ -35,9 +35,8 @@ runs: LOG_VERBOSITY: ${{ inputs.log_verbosity }} SANITIZERS: ${{ inputs.sanitizers }} run: | - echo 'Installing dependencies.' conan install \ - --profile ci \ + --profile:all ci \ --build="${BUILD_OPTION}" \ --options:host='&:tests=True' \ --options:host='&:xrpld=True' \ diff --git a/.github/actions/set-compiler-env/action.yml b/.github/actions/set-compiler-env/action.yml new file mode 100644 index 0000000000..a16dde2b30 --- /dev/null +++ b/.github/actions/set-compiler-env/action.yml @@ -0,0 +1,34 @@ +name: Set compiler environment +description: "Set CC and CXX environment variables for the given compiler." + +inputs: + compiler: + description: 'The compiler to use ("gcc" or "clang").' + required: true + +runs: + using: composite + + steps: + - name: Set CC and CXX for gcc + if: ${{ inputs.compiler == 'gcc' }} + shell: bash + run: | + echo "CC=gcc" >>"${GITHUB_ENV}" + echo "CXX=g++" >>"${GITHUB_ENV}" + + - name: Set CC and CXX for clang + if: ${{ inputs.compiler == 'clang' }} + shell: bash + run: | + echo "CC=clang" >>"${GITHUB_ENV}" + echo "CXX=clang++" >>"${GITHUB_ENV}" + + - name: Fail on unknown compiler + if: ${{ inputs.compiler != 'gcc' && inputs.compiler != 'clang' }} + shell: bash + env: + COMPILER: ${{ inputs.compiler }} + run: | + echo "Unknown compiler: $COMPILER" >&2 + exit 1 diff --git a/.github/actions/setup-conan/action.yml b/.github/actions/setup-conan/action.yml index 9d834884d2..0dd22f0d92 100644 --- a/.github/actions/setup-conan/action.yml +++ b/.github/actions/setup-conan/action.yml @@ -15,32 +15,35 @@ runs: using: composite steps: - - name: Set up Conan configuration + - name: Apply custom configuration to global.conf shell: bash run: | - echo 'Installing configuration.' cat conan/global.conf ${{ runner.os == 'Linux' && '>>' || '>' }} $(conan config home)/global.conf - echo 'Conan configuration:' - conan config show '*' - - - name: Set up Conan profile + - name: Show global configuration + shell: bash + run: | + conan config show '*' + + - name: Install profiles shell: bash run: | - echo 'Installing profile.' conan config install conan/profiles/ -tf $(conan config home)/profiles/ - echo 'Conan profile:' + - name: Show CI profile + shell: bash + run: | conan profile show --profile ci - - name: Set up Conan remote + - name: Add a remote shell: bash env: REMOTE_NAME: ${{ inputs.remote_name }} REMOTE_URL: ${{ inputs.remote_url }} run: | - echo "Adding Conan remote '${REMOTE_NAME}' at '${REMOTE_URL}'." conan remote add --index 0 --force "${REMOTE_NAME}" "${REMOTE_URL}" - echo 'Listing Conan remotes.' + - name: List remotes + shell: bash + run: | conan remote list diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 6eccfcc6be..aaf84a51d0 100755 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -1,384 +1,281 @@ #!/usr/bin/env python3 import argparse +import dataclasses import itertools import json -from dataclasses import dataclass from pathlib import Path THIS_DIR = Path(__file__).parent.resolve() +_BASE_CMAKE_ARGS = ["-Dtests=ON", "-Dwerr=ON", "-Dxrpld=ON", "-Dwextra=ON"] -@dataclass -class Config: - architecture: list[dict] - os: list[dict] +# Maps sanitizer names (as used in cmake) to short config-name suffixes. +_SANITIZER_SUFFIX: dict[str, str] = { + "address": "asan", + "undefinedbehavior": "ubsan", + "thread": "tsan", +} + + +def get_cmake_args(build_type: str, extra_args: str) -> str: + """Get the full list of CMake arguments for a config.""" + args = _BASE_CMAKE_ARGS.copy() + if build_type == "Release": + args.append("-Dassert=ON") + if extra_args: + args.extend(extra_args.split()) + return " ".join(args) + + +# --------------------------------------------------------------------------- +# Input types — shapes of the JSON config files +# --------------------------------------------------------------------------- + + +@dataclasses.dataclass +class LinuxConfig: + """One entry in linux.json's 'configs' or 'package_configs' arrays.""" + + compiler: list[str] build_type: list[str] - cmake_args: list[str] + arch: list[str] + sanitizers: list[str] = dataclasses.field(default_factory=list) + suffix: str = "" + extra_cmake_args: str = "" + image: str = "" # only used by package_configs entries -""" -Generate a strategy matrix for GitHub Actions CI. +@dataclasses.dataclass +class LinuxFile: + """Shape of linux.json.""" -On each PR commit we will build a selection of Debian, RHEL, Ubuntu, MacOS, and -Windows configurations, while upon merge into the develop or release branches, -we will build all configurations, and test most of them. + image_tag: str + configs: dict[str, list[LinuxConfig]] # distro → configs + package_configs: dict[str, list[LinuxConfig]] # distro → packaging configs -We will further set additional CMake arguments as follows: -- All builds will have the `tests`, `werr`, and `xrpld` options. -- All builds will have the `wextra` option except for GCC 12 and Clang 16. -- All release builds will have the `assert` option. -- Certain Debian Bookworm configurations will change the reference fee, enable - codecov, and enable voidstar in PRs. -""" + @classmethod + def load(cls, path: Path) -> "LinuxFile": + data = json.loads(path.read_text()) + + def parse(section: dict) -> dict[str, list[LinuxConfig]]: + return { + distro: [LinuxConfig(**c) for c in cfgs] + for distro, cfgs in section.items() + } + + return cls( + image_tag=data["image_tag"], + configs=parse(data["configs"]), + package_configs=parse(data.get("package_configs", {})), + ) -def build_config_name(os_entry: dict[str, str], platform: str, build_type: str) -> str: - parts = [os_entry["distro_name"]] - for key in ("distro_version", "compiler_name", "compiler_version"): - if value := os_entry[key]: - parts.append(value) - parts.append("arm64" if "arm64" in platform else "amd64") - parts.append(build_type.lower()) - return "-".join(parts) +@dataclasses.dataclass +class PlatformConfig: + """One entry in macos.json's or windows.json's 'configs' array.""" + + build_type: list[str] + build_only: bool = False # if true, skip tests (e.g. macos/Windows Debug) + extra_cmake_args: str = "" + + def __post_init__(self) -> None: + if isinstance(self.build_type, str): + self.build_type = [self.build_type] -def generate_packaging_matrix(config: Config) -> list[dict]: - """Emit one entry per os entry with `package: true`. Architecture is - hardcoded to linux/amd64 here (and the runner is hardcoded at the - workflow level) until arm64 packaging is ready. +@dataclasses.dataclass +class PlatformFile: + """Shape of macos.json and windows.json.""" + + platform: str # e.g. "macos/arm64" or "windows/amd64" + runner: list[str] # GitHub Actions runner labels + configs: list[PlatformConfig] + + @classmethod + def load(cls, path: Path) -> "PlatformFile": + data = json.loads(path.read_text()) + return cls( + platform=data["platform"], + runner=data["runner"], + configs=[PlatformConfig(**c) for c in data["configs"]], + ) + + +# --------------------------------------------------------------------------- +# Output types — shapes of the generated GitHub Actions matrix entries +# --------------------------------------------------------------------------- + + +@dataclasses.dataclass +class Architecture: + platform: str + runner: list[str] + + +@dataclasses.dataclass +class MatrixEntry: + """One entry in the generated build/test strategy matrix.""" + + config_name: str + cmake_args: str + cmake_target: str + build_only: bool + build_type: str + architecture: Architecture + sanitizers: str + image: str = "" # container image; empty for macOS/Windows (runs natively) + compiler: str = "" # compiler name ("gcc" or "clang"); empty for macOS/Windows + + +@dataclasses.dataclass +class PackagingEntry: + """One entry in the generated packaging strategy matrix.""" + + artifact_name: str + image: str + distro: str # e.g. "debian" or "rhel"; drives package-format-specific steps + + +# --------------------------------------------------------------------------- +# Matrix expansion +# --------------------------------------------------------------------------- + +_ARCHS: dict[str, Architecture] = { + "amd64": Architecture( + platform="linux/amd64", runner=["self-hosted", "Linux", "X64", "heavy"] + ), + "arm64": Architecture( + platform="linux/arm64", + runner=["self-hosted", "Linux", "ARM64", "heavy-arm64"], + ), +} + + +def expand_linux_matrix(linux: LinuxFile) -> list[MatrixEntry]: + """Expand a LinuxFile into a flat list of matrix entries. + + Each config entry is expanded over the cross-product of its + compiler, build_type, sanitizers, and architecture lists. """ - return [ - { - "artifact_name": f"xrpld-{build_config_name(os, 'linux/amd64', 'Release')}", - "os": os, - } - for os in config.os - if os.get("package", False) - ] + entries: list[MatrixEntry] = [] + for distro, configs in linux.configs.items(): + for cfg in configs: + # An empty sanitizers list means "one entry with no sanitizer". + effective_sanitizers = cfg.sanitizers or [""] + effective_archs = {arch: _ARCHS[arch] for arch in cfg.arch} -def generate_strategy_matrix(all: bool, config: Config) -> list[dict]: - configurations = [] - for architecture, os, build_type, cmake_args in itertools.product( - config.architecture, config.os, config.build_type, config.cmake_args - ): - # The default CMake target is 'all' for Linux and MacOS and 'install' - # for Windows, but it can get overridden for certain configurations. - cmake_target = "install" if os["distro_name"] == "windows" else "all" - - # We build and test all configurations by default, except for Windows in - # Debug, because it is too slow, as well as when code coverage is - # enabled as that mode already runs the tests. - build_only = False - if os["distro_name"] == "windows" and build_type == "Debug": - build_only = True - - # Only generate a subset of configurations in PRs. - if not all: - # Debian: - # - Bookworm using GCC 13: Debug on linux/amd64, set the reference - # fee to 500 and enable code coverage (which will be done below). - # - Bookworm using GCC 15: Debug on linux/amd64, enable Address and - # UB sanitizers (which will be done below). - # - Bookworm using Clang 16: Debug on linux/amd64, enable voidstar. - # - Bookworm using Clang 17: Release on linux/amd64, set the - # reference fee to 1000. - # - Bookworm using Clang 20: Debug on linux/amd64, enable Address - # and UB sanitizers (which will be done below). - if os["distro_name"] == "debian": - skip = True - if os["distro_version"] == "bookworm": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}" - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-16" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"-Dvoidstar=ON {cmake_args}" - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-17" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=1000 {cmake_args}" - skip = False - elif os["distro_version"] == "trixie": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-22" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if skip: - continue - - # RHEL: - # - 9 using GCC 12: Debug and Release on linux/amd64 - # (Release is required for RPM packaging). - # - 10 using Clang: Release on linux/amd64. - if os["distro_name"] == "rhel": - skip = True - if os["distro_version"] == "9": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type in ["Debug", "Release"] - and architecture["platform"] == "linux/amd64" - ): - skip = False - elif os["distro_version"] == "10": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-any" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if skip: - continue - - # Ubuntu: - # - Jammy using GCC 12: Debug on linux/arm64, Release on - # linux/amd64 (Release is required for DEB packaging). - # - Noble using GCC 14: Release on linux/amd64. - # - Noble using Clang 18: Debug on linux/amd64. - # - Noble using Clang 19: Release on linux/arm64. - if os["distro_name"] == "ubuntu": - skip = True - if os["distro_version"] == "jammy": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type == "Debug" - and architecture["platform"] == "linux/arm64" - ): - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - skip = False - elif os["distro_version"] == "noble": - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-14" - and build_type == "Release" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-18" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - skip = False - if ( - f"{os['compiler_name']}-{os['compiler_version']}" == "clang-19" - and build_type == "Release" - and architecture["platform"] == "linux/arm64" - ): - skip = False - if skip: - continue - - # MacOS: - # - Debug on macos/arm64. - if os["distro_name"] == "macos" and not ( - build_type == "Debug" and architecture["platform"] == "macos/arm64" + for compiler, build_type, sanitizer, (arch, arch_info) in itertools.product( + cfg.compiler, + cfg.build_type, + effective_sanitizers, + effective_archs.items(), ): - continue + name = f"{distro}-{compiler}-{build_type.lower()}-{arch}" + suffix_parts = [ + s for s in [cfg.suffix, _SANITIZER_SUFFIX.get(sanitizer, "")] if s + ] + if suffix_parts: + name += "-" + "-".join(suffix_parts) - # Windows: - # - Release on windows/amd64. - if os["distro_name"] == "windows" and not ( - build_type == "Release" and architecture["platform"] == "windows/amd64" - ): - continue - - # Additional CMake arguments. - cmake_args = f"{cmake_args} -Dtests=ON -Dwerr=ON -Dxrpld=ON" - if not f"{os['compiler_name']}-{os['compiler_version']}" in [ - "gcc-12", - "clang-16", - ]: - cmake_args = f"{cmake_args} -Dwextra=ON" - if build_type == "Release": - cmake_args = f"{cmake_args} -Dassert=ON" - - # We skip all RHEL on arm64 due to a build failure that needs further - # investigation. - if os["distro_name"] == "rhel" and architecture["platform"] == "linux/arm64": - continue - - # We skip all clang 20+ on arm64 due to Boost build error. - if ( - os["compiler_name"] == "clang" - and os["compiler_version"].isdigit() - and int(os["compiler_version"]) >= 20 - and architecture["platform"] == "linux/arm64" - ): - continue - - # Enable code coverage for Debian Bookworm using GCC 13 in Debug on - # linux/amd64. - if ( - f"{os['distro_name']}-{os['distro_version']}" == "debian-bookworm" - and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"{cmake_args} -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0" - - # Enable unity build for Ubuntu Jammy using GCC 12 in Debug on - # linux/amd64. - if ( - f"{os['distro_name']}-{os['distro_version']}" == "ubuntu-jammy" - and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12" - and build_type == "Debug" - and architecture["platform"] == "linux/amd64" - ): - cmake_args = f"{cmake_args} -Dunity=ON" - - # Generate a unique name for the configuration, e.g. macos-arm64-debug - # or debian-bookworm-gcc-12-amd64-release. - config_name = build_config_name(os, architecture["platform"], build_type) - if "-Dcoverage=ON" in cmake_args: - config_name += "-coverage" - if "-Dunity=ON" in cmake_args: - config_name += "-unity" - - # Add the configuration to the list, with the most unique fields first, - # so that they are easier to identify in the GitHub Actions UI, as long - # names get truncated. - # Add Address and UB sanitizers as separate configurations for specific - # bookworm distros. Thread sanitizer is currently disabled (see below). - # GCC-Asan xrpld-embedded tests are failing because of https://github.com/google/sanitizers/issues/856 - if ( - os["distro_version"] == "bookworm" - and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15" - ) or ( - os["distro_version"] == "trixie" - and f"{os['compiler_name']}-{os['compiler_version']}" == "clang-22" - ): - # Add ASAN and UBSAN configurations for both gcc-15 and clang-22 - configurations.append( - { - "config_name": config_name + "-asan", - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "address", - } - ) - configurations.append( - { - "config_name": config_name + "-ubsan", - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "undefinedbehavior", - } - ) - # TSAN is deactivated due to seg faults with latest compilers. - activate_tsan = False - if activate_tsan: - configurations.append( - { - "config_name": config_name + "-tsan-ubsan", - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "thread,undefinedbehavior", - } + entries.append( + MatrixEntry( + config_name=name, + image=f"ghcr.io/xrplf/xrpld/nix-{distro}:{linux.image_tag}", + cmake_args=get_cmake_args(build_type, cfg.extra_cmake_args), + cmake_target="all", + build_only=False, + build_type=build_type, + architecture=arch_info, + sanitizers=sanitizer, + compiler=compiler, + ) + ) + + return entries + + +def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]: + """Generate the packaging matrix from a LinuxFile's package_configs section. + + Packaging uses vanilla distro images (debian:bookworm, ubi9, …) instead of + the nix-based build images, because deb/rpm tooling (debhelper, rpm-build) + is taken from the distro's archive rather than from nixpkgs. Each config + entry carries its own 'image'. + """ + entries = [] + for distro, configs in linux.package_configs.items(): + for cfg in configs: + for compiler, build_type in itertools.product(cfg.compiler, cfg.build_type): + entries.append( + PackagingEntry( + artifact_name=f"xrpld-{distro}-{compiler}-{build_type.lower()}-amd64", + image=cfg.image, + distro=distro, + ) + ) + + return entries + + +def expand_platform_matrix(pf: PlatformFile) -> list[MatrixEntry]: + """Expand a PlatformFile (macOS or Windows) into matrix entries.""" + platform_name, arch = pf.platform.split("/") + is_windows = platform_name == "windows" + + entries: list[MatrixEntry] = [] + for cfg in pf.configs: + for build_type in cfg.build_type: + entries.append( + MatrixEntry( + config_name=f"{platform_name}-{arch}-{build_type.lower()}", + cmake_args=get_cmake_args(build_type, cfg.extra_cmake_args), + cmake_target="install" if is_windows else "all", + build_only=cfg.build_only, + build_type=build_type, + architecture=Architecture(platform=pf.platform, runner=pf.runner), + sanitizers="", ) - else: - configurations.append( - { - "config_name": config_name, - "cmake_args": cmake_args, - "cmake_target": cmake_target, - "build_only": build_only, - "build_type": build_type, - "os": os, - "architecture": architecture, - "sanitizers": "", - } ) - - return configurations + return entries -def read_config(file: Path) -> Config: - config = json.loads(file.read_text()) - if ( - config["architecture"] is None - or config["os"] is None - or config["build_type"] is None - or config["cmake_args"] is None - ): - raise Exception("Invalid configuration file.") - - return Config(**config) +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument( - "-a", - "--all", - help="Set to generate all configurations (generally used when merging a PR) or leave unset to generate a subset of configurations (generally used when committing to a PR).", - action="store_true", + parser = argparse.ArgumentParser( + description="Generate a CI strategy matrix for all platforms or a specific one." ) parser.add_argument( "-c", "--config", - help="Path to the JSON file containing the strategy matrix configurations.", - required=False, - type=Path, + help="Platform to generate for ('linux', 'macos', or 'windows'). Defaults to all platforms.", + choices=["linux", "macos", "windows"], + default=None, ) parser.add_argument( "-p", "--packaging", - help="Emit the packaging matrix (derived from the 'package' field on os entries) instead of the build/test matrix.", + help="Emit the Linux packaging matrix instead of the build/test matrix.", action="store_true", ) args = parser.parse_args() - matrix = [] - if args.packaging: - config_path = args.config if args.config else THIS_DIR / "linux.json" - matrix += generate_packaging_matrix(read_config(config_path)) - elif args.config is None or args.config == "": - matrix += generate_strategy_matrix( - args.all, read_config(THIS_DIR / "linux.json") - ) - matrix += generate_strategy_matrix( - args.all, read_config(THIS_DIR / "macos.json") - ) - matrix += generate_strategy_matrix( - args.all, read_config(THIS_DIR / "windows.json") - ) - else: - matrix += generate_strategy_matrix(args.all, read_config(args.config)) + matrix: list[MatrixEntry] | list[PackagingEntry] = [] - # Generate the strategy matrix. - print(f"matrix={json.dumps({'include': matrix})}") + if args.packaging: + matrix = expand_linux_packaging(LinuxFile.load(THIS_DIR / "linux.json")) + else: + if args.config in ("linux", None): + matrix += expand_linux_matrix(LinuxFile.load(THIS_DIR / "linux.json")) + if args.config in ("macos", None): + matrix += expand_platform_matrix(PlatformFile.load(THIS_DIR / "macos.json")) + if args.config in ("windows", None): + matrix += expand_platform_matrix( + PlatformFile.load(THIS_DIR / "windows.json") + ) + + print(f"matrix={json.dumps({'include': [dataclasses.asdict(e) for e in matrix]})}") diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index 4f090a81a3..3070b8d9f4 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -1,221 +1,83 @@ { - "architecture": [ - { - "platform": "linux/amd64", - "runner": ["self-hosted", "Linux", "X64", "heavy"] - }, - { - "platform": "linux/arm64", - "runner": ["self-hosted", "Linux", "ARM64", "heavy-arm64"] - } - ], - "os": [ - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "12", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "13", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "gcc", - "compiler_version": "15", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "16", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "17", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "18", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "19", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "bookworm", - "compiler_name": "clang", - "compiler_version": "20", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "gcc", - "compiler_version": "15", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "clang", - "compiler_version": "20", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "clang", - "compiler_version": "21", - "image_sha": "4c086b9" - }, - { - "distro_name": "debian", - "distro_version": "trixie", - "compiler_name": "clang", - "compiler_version": "22", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "8", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "8", - "compiler_name": "clang", - "compiler_version": "any", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "gcc", - "compiler_version": "12", - "image_sha": "4c086b9", - "package": true - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "gcc", - "compiler_version": "13", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "9", - "compiler_name": "clang", - "compiler_version": "any", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "10", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "rhel", - "distro_version": "10", - "compiler_name": "clang", - "compiler_version": "any", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "jammy", - "compiler_name": "gcc", - "compiler_version": "12", - "image_sha": "4c086b9", - "package": true - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "gcc", - "compiler_version": "13", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "gcc", - "compiler_version": "14", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "16", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "17", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "18", - "image_sha": "4c086b9" - }, - { - "distro_name": "ubuntu", - "distro_version": "noble", - "compiler_name": "clang", - "compiler_version": "19", - "image_sha": "4c086b9" - } - ], - "build_type": ["Debug", "Release"], - "cmake_args": [""] + "image_tag": "sha-6c54342", + "configs": { + "ubuntu": [ + { + "compiler": ["gcc", "clang"], + "build_type": ["Debug", "Release"], + "arch": ["amd64", "arm64"] + }, + + { + "compiler": ["gcc", "clang"], + "build_type": ["Debug"], + "arch": ["amd64"], + "sanitizers": ["address", "undefinedbehavior"] + }, + + { + "compiler": ["gcc"], + "build_type": ["Debug"], + "arch": ["amd64"], + "suffix": "coverage", + "extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=500 -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0" + }, + { + "compiler": ["clang"], + "build_type": ["Debug"], + "arch": ["amd64"], + "suffix": "voidstar", + "extra_cmake_args": "-Dvoidstar=ON" + }, + { + "compiler": ["clang"], + "build_type": ["Release"], + "arch": ["amd64"], + "suffix": "reffee", + "extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=1000" + }, + { + "compiler": ["gcc"], + "build_type": ["Debug"], + "arch": ["amd64"], + "suffix": "unity", + "extra_cmake_args": "-Dunity=ON" + } + ], + + "debian": [ + { + "compiler": ["gcc"], + "build_type": ["Release"], + "arch": ["amd64"] + } + ], + + "rhel": [ + { + "compiler": ["gcc"], + "build_type": ["Release"], + "arch": ["amd64"] + } + ] + }, + "package_configs": { + "debian": [ + { + "compiler": ["gcc"], + "build_type": ["Release"], + "arch": ["amd64"], + "image": "debian:bookworm" + } + ], + + "rhel": [ + { + "compiler": ["gcc"], + "build_type": ["Release"], + "arch": ["amd64"], + "image": "registry.access.redhat.com/ubi9/ubi:latest" + } + ] + } } diff --git a/.github/scripts/strategy-matrix/macos.json b/.github/scripts/strategy-matrix/macos.json index 6fc44d0f80..5b9e32f88e 100644 --- a/.github/scripts/strategy-matrix/macos.json +++ b/.github/scripts/strategy-matrix/macos.json @@ -1,19 +1,15 @@ { - "architecture": [ + "platform": "macos/arm64", + "runner": ["self-hosted", "macOS", "ARM64", "mac-runner-m1"], + "configs": [ { - "platform": "macos/arm64", - "runner": ["self-hosted", "macOS", "ARM64", "mac-runner-m1"] - } - ], - "os": [ + "build_type": "Release", + "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5" + }, { - "distro_name": "macos", - "distro_version": "", - "compiler_name": "", - "compiler_version": "", - "image_sha": "" + "build_type": "Debug", + "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5", + "build_only": true } - ], - "build_type": ["Debug", "Release"], - "cmake_args": ["-DCMAKE_POLICY_VERSION_MINIMUM=3.5"] + ] } diff --git a/.github/scripts/strategy-matrix/windows.json b/.github/scripts/strategy-matrix/windows.json index 8c536c70f2..e4678b60db 100644 --- a/.github/scripts/strategy-matrix/windows.json +++ b/.github/scripts/strategy-matrix/windows.json @@ -1,19 +1,8 @@ { - "architecture": [ - { - "platform": "windows/amd64", - "runner": ["self-hosted", "Windows", "devbox"] - } - ], - "os": [ - { - "distro_name": "windows", - "distro_version": "", - "compiler_name": "", - "compiler_version": "", - "image_sha": "" - } - ], - "build_type": ["Debug", "Release"], - "cmake_args": [""] + "platform": "windows/amd64", + "runner": ["self-hosted", "Windows", "devbox"], + "configs": [ + { "build_type": "Release" }, + { "build_type": "Debug", "build_only": true } + ] } diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml index b7517ccf11..42d5827cab 100644 --- a/.github/workflows/on-tag.yml +++ b/.github/workflows/on-tag.yml @@ -33,7 +33,6 @@ jobs: with: ccache_enabled: false os: ${{ matrix.os }} - strategy_matrix: minimal secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index 803ba3c87b..74bca82019 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -88,7 +88,6 @@ jobs: # not identical to a regular compilation. ccache_enabled: ${{ github.repository_owner == 'XRPLF' && !startsWith(github.ref, 'refs/heads/release') }} os: ${{ matrix.os }} - strategy_matrix: ${{ github.event_name == 'schedule' && 'all' || 'minimal' }} secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 31457bb892..e1154f74be 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -57,6 +57,12 @@ on: type: string default: "" + compiler: + description: 'The compiler to use ("gcc" or "clang"). Leave empty for macOS/Windows (uses system default).' + required: false + type: string + default: "" + secrets: CODECOV_TOKEN: description: "The Codecov token to use for uploading coverage reports." @@ -124,6 +130,12 @@ jobs: with: subtract: ${{ inputs.nproc_subtract }} + - name: Set compiler environment (Linux) + if: ${{ runner.os == 'Linux' }} + uses: ./.github/actions/set-compiler-env + with: + compiler: ${{ inputs.compiler }} + - name: Setup Conan env: SANITIZERS: ${{ inputs.sanitizers }} @@ -191,6 +203,21 @@ jobs: --parallel "${BUILD_NPROC}" \ --target "${CMAKE_TARGET}" + # This step is needed to allow running in non-Nix environments + - name: Patch binary to use default loader and remove rpath (Linux) + if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }} + run: | + loader="$(/tmp/loader-path.sh)" + patchelf --set-interpreter "${loader}" --remove-rpath "${{ env.BUILD_DIR }}/xrpld" + + # We're only running aarch64 Linux builds in Ubuntu-based images, so this is kept simple + - name: Install libatomic (Linux aarch64) + if: ${{ runner.os == 'Linux' && runner.arch == 'ARM64' }} + run: | + apt update --yes + apt install -y --no-install-recommends \ + libatomic1 + - name: Show ccache statistics if: ${{ inputs.ccache_enabled }} run: | @@ -217,7 +244,7 @@ jobs: ./xrpld --definitions | python3 -m json.tool >server_definitions.json - name: Upload server definitions - if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'debian-bookworm-gcc-13-amd64-release' }} + if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'debian-gcc-release-amd64' }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: server-definitions @@ -279,7 +306,25 @@ jobs: set -o pipefail # Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$((BUILD_NPROC - 2)) - ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log + + # The resolver/preload workaround is only correct for the ASan build: + # a regular build doesn't hit the __dn_expand interceptor bug, and must + # NOT have libasan injected. So only preload when xrpld is ASan-built. + # + # libresolv hosts getaddrinfo's resolver helpers (dn_expand, res_*). Under ASan + # these are intercepted via dlsym(RTLD_NEXT, ...), which yields a NULL pointer + # and crashes DNS resolution if libresolv isn't loaded. Linking it guarantees + # the symbols are present; it's a harmless no-op on glibc >= 2.34 (merged into + # libc) and is what the compiler driver already does for sanitizer builds. + # https://github.com/llvm/llvm-project/issues/59007 + # https://github.com/google/sanitizers/issues/1592 + if ldd ./xrpld | grep -q libasan; then + PRELOAD="$(gcc -print-file-name=libasan.so):/usr/lib/x86_64-linux-gnu/libresolv.so.2" + else + PRELOAD="" + fi + + LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log - name: Show test failure summary if: ${{ failure() && !inputs.build_only }} diff --git a/.github/workflows/reusable-build-test.yml b/.github/workflows/reusable-build-test.yml index 0086cbbfb5..4b64c53521 100644 --- a/.github/workflows/reusable-build-test.yml +++ b/.github/workflows/reusable-build-test.yml @@ -19,13 +19,6 @@ on: required: true type: string - strategy_matrix: - # TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations. - description: 'The strategy matrix to use for generating the configurations ("minimal", "all").' - required: false - type: string - default: "minimal" - secrets: CODECOV_TOKEN: description: "The Codecov token to use for uploading coverage reports." @@ -37,7 +30,6 @@ jobs: uses: ./.github/workflows/reusable-strategy-matrix.yml with: os: ${{ inputs.os }} - strategy_matrix: ${{ inputs.strategy_matrix }} # Build and test the binary for each configuration. build-test-config: @@ -47,7 +39,6 @@ jobs: strategy: fail-fast: ${{ github.event_name == 'merge_group' }} matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} - max-parallel: 10 with: build_only: ${{ matrix.build_only }} build_type: ${{ matrix.build_type }} @@ -55,8 +46,9 @@ jobs: cmake_args: ${{ matrix.cmake_args }} cmake_target: ${{ matrix.cmake_target }} runs_on: ${{ toJSON(matrix.architecture.runner) }} - image: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || '' }} + image: ${{ matrix.image || '' }} config_name: ${{ matrix.config_name }} sanitizers: ${{ matrix.sanitizers }} + compiler: ${{ matrix.compiler || '' }} secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index 2a3ed8a33e..670c01733e 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -1,8 +1,7 @@ # Build Linux packages (DEB and RPM) from pre-built binary artifacts. -# Discovers which configurations to package from linux.json (os entries -# with "package": true) and fans out one job per entry. Today only -# linux/amd64 is emitted; the architecture is hardcoded both here -# (runner) and in generate.py. +# Discovers which configurations to package from linux.json (configs in +# "package_configs") and fans out one job per distro. Only linux/amd64 is +# supported; the runner is hardcoded in the job below. name: Package on: @@ -33,13 +32,12 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: 3.13 + python-version: "3.13" - name: Generate packaging matrix id: generate working-directory: .github/scripts/strategy-matrix - run: | - ./generate.py --packaging --config=linux.json >>"${GITHUB_OUTPUT}" + run: ./generate.py --packaging >>"${GITHUB_OUTPUT}" generate-version: runs-on: ubuntu-latest @@ -66,10 +64,35 @@ jobs: permissions: contents: read runs-on: ["self-hosted", "Linux", "X64", "heavy"] - container: ${{ format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) }} + container: ${{ matrix.image }} timeout-minutes: 30 steps: + # Packaging runs in a vanilla distro image, so the tooling has to come + # from the distro's archive: debhelper for deb, rpm-build (and the + # systemd / find-debuginfo macros it depends on) for rpm. Run this + # before actions/checkout so the latter can use git (real history) for + # build_pkg.sh's SOURCE_DATE_EPOCH; otherwise it falls back to a tarball + # download and the timestamp comes from wall-clock time. + - name: Install packaging tooling (deb) + if: ${{ matrix.distro == 'debian' }} + run: | + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y --no-install-recommends \ + ca-certificates \ + debhelper \ + git + + - name: Install packaging tooling (rpm) + if: ${{ matrix.distro == 'rhel' }} + run: | + dnf install -y --setopt=install_weak_deps=False \ + git \ + rpm-build \ + redhat-rpm-config \ + systemd-rpm-macros + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index 62d65ad3fa..16a2b4e336 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -4,15 +4,9 @@ on: workflow_call: inputs: os: - description: 'The operating system to use for the build ("linux", "macos", "windows").' + description: 'The operating system to use for the build ("linux", "macos", "windows", or empty for all).' required: false type: string - strategy_matrix: - # TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations. - description: 'The strategy matrix to use for generating the configurations ("minimal", "all").' - required: false - type: string - default: "minimal" outputs: matrix: description: "The generated strategy matrix." @@ -34,12 +28,11 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: 3.13 + python-version: "3.13" - name: Generate strategy matrix working-directory: .github/scripts/strategy-matrix id: generate env: - GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} - GENERATE_OPTION: ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} - run: ./generate.py ${GENERATE_OPTION} ${GENERATE_CONFIG} >>"${GITHUB_OUTPUT}" + GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}', inputs.os) || '' }} + run: ./generate.py ${GENERATE_CONFIG} >>"${GITHUB_OUTPUT}" diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index 34dce28334..87465b4d3d 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -48,8 +48,6 @@ jobs: # Generate the strategy matrix to be used by the following job. generate-matrix: uses: ./.github/workflows/reusable-strategy-matrix.yml - with: - strategy_matrix: ${{ github.event_name == 'pull_request' && 'minimal' || 'all' }} # Build and upload the dependencies for each configuration. run-upload-conan-deps: @@ -58,9 +56,8 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} - max-parallel: 10 runs-on: ${{ matrix.architecture.runner }} - container: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || null }} + container: ${{ matrix.image || null }} steps: - name: Cleanup workspace (macOS and Windows) if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }} @@ -83,6 +80,12 @@ jobs: with: subtract: ${{ env.NPROC_SUBTRACT }} + - name: Set compiler environment (Linux) + if: ${{ runner.os == 'Linux' }} + uses: ./.github/actions/set-compiler-env + with: + compiler: ${{ matrix.compiler }} + - name: Setup Conan env: SANITIZERS: ${{ matrix.sanitizers }} diff --git a/cmake/XrplCompiler.cmake b/cmake/XrplCompiler.cmake index 0b77ff3525..9af8e962d0 100644 --- a/cmake/XrplCompiler.cmake +++ b/cmake/XrplCompiler.cmake @@ -145,13 +145,39 @@ else() INTERFACE -rdynamic $<$:-Wl,-z,relro,-z,now,--build-id> - # link to static libc/c++ iff: * static option set and * NOT APPLE (AppleClang does not support static - # libc/c++) and * NOT SANITIZERS (sanitizers typically don't work with static libc/c++) - $<$,$>,$>>: + # link to static libc/c++ if: + # * static option set and + # * NOT APPLE (AppleClang does not support static libc/c++) + $<$,$>>: -static-libstdc++ -static-libgcc > ) + + # Keep -stdlib=libstdc++ off the compile commands, but preserve it for linking. + # + # Conan turns `compiler.libcxx=libstdc++` into `-stdlib=libstdc++` and puts it in + # CMAKE_CXX_FLAGS, which CMake passes to BOTH compile and link steps. On a normal Clang + # the compile step consumes it while choosing the C++ stdlib include paths. The Nixpkgs + # Clang wrapper supplies those paths itself (via -nostdinc++), so at compile time the + # flag is unused -> Clang errors under our -Werror. At link time the flag IS consumed + # (it selects the C++ runtime), so we move it there instead of dropping it entirely. + get_filename_component(_cxx_real "${CMAKE_CXX_COMPILER}" REALPATH) + if( + _cxx_real MATCHES "^/nix/store/" + AND is_linux + AND is_clang + AND CMAKE_CXX_FLAGS MATCHES "stdlib=libstdc" + ) + string( + REPLACE "-stdlib=libstdc++" + "" + CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS}" + ) + string(STRIP "${CMAKE_CXX_FLAGS}" CMAKE_CXX_FLAGS) + add_link_options($<$:-stdlib=libstdc++>) + endif() endif() # Antithesis instrumentation will only be built and deployed using machines running Linux. diff --git a/conan/profiles/ci b/conan/profiles/ci index ae93187026..9422addfe3 100644 --- a/conan/profiles/ci +++ b/conan/profiles/ci @@ -1 +1,8 @@ +{% set os = detect_api.detect_os() %} include(sanitizers) + +[conf] +{% if os == "Linux" %} +user.package:libc_version=2.31 +tools.info.package_id:confs+=["user.package:libc_version"] +{% endif %} diff --git a/cspell.config.yaml b/cspell.config.yaml index da5dc9b072..cab2fc3da6 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -50,6 +50,7 @@ words: - AMMXRP - amt - amts + - archs - asnode - asynchrony - attestation diff --git a/docker/check-tools.sh b/docker/check-tools.sh index c446dc1b4a..faa6520678 100755 --- a/docker/check-tools.sh +++ b/docker/check-tools.sh @@ -10,7 +10,6 @@ cmake --version conan --version curl --version doxygen --version -dpkg-buildpackage --version g++ --version gcc --version gcov --version @@ -26,7 +25,6 @@ perl --version pkg-config --version pre-commit --version python3 --version -rpmbuild --version run-clang-tidy --help vim --version diff --git a/nix/packages.nix b/nix/packages.nix index c51077367e..6a83446d88 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -13,7 +13,6 @@ in conan curlMinimal # needed for codecov/codecov-action doxygen - dpkg # needed for dpkg-buildpackage gcovr git gnumake @@ -28,7 +27,6 @@ in pkg-config pre-commit python3 - rpm # needed for rpmbuild runClangTidy vim ]; From 2111bb4b9593a5c4f4a43ad1a27cde310159bdf5 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Fri, 5 Jun 2026 15:11:47 +0100 Subject: [PATCH 63/87] ci: Update clang-tidy to nix-based v22 (#7412) --- .clang-tidy | 2 +- .github/workflows/reusable-clang-tidy.yml | 7 +- include/xrpl/nodestore/detail/varint.h | 2 +- src/libxrpl/basics/Log.cpp | 3 +- src/libxrpl/basics/Number.cpp | 8 +- src/libxrpl/beast/core/CurrentThreadName.cpp | 1 - src/libxrpl/server/Port.cpp | 4 +- src/test/app/AccountDelete_test.cpp | 2 +- src/test/app/Batch_test.cpp | 972 +++++++++++++++--- src/test/app/Credentials_test.cpp | 4 +- src/test/app/CrossingLimitsMPT_test.cpp | 4 +- src/test/app/CrossingLimits_test.cpp | 4 +- src/test/app/DepositAuth_test.cpp | 87 +- src/test/app/Escrow_test.cpp | 4 +- src/test/app/LedgerReplay_test.cpp | 2 +- src/test/app/PayStrand_test.cpp | 28 +- src/test/app/PermissionedDEX_test.cpp | 6 +- src/test/app/PermissionedDomains_test.cpp | 98 +- src/test/app/TxQ_test.cpp | 2 +- src/test/app/ValidatorList_test.cpp | 87 +- src/test/app/ValidatorSite_test.cpp | 391 ++++--- src/test/basics/PerfLog_test.cpp | 2 +- .../beast/aged_associative_container_test.cpp | 6 +- src/test/core/Config_test.cpp | 16 +- src/test/jtx/impl/WSClient.cpp | 2 +- src/test/jtx/impl/permissioned_dex.cpp | 2 +- src/test/jtx/impl/permissioned_domains.cpp | 4 +- src/test/nodestore/import_test.cpp | 6 +- src/test/rpc/AccountObjects_test.cpp | 4 +- src/test/rpc/DepositAuthorized_test.cpp | 4 +- src/test/rpc/LedgerEntry_test.cpp | 49 +- src/xrpld/app/misc/detail/ValidatorList.cpp | 2 +- .../peerfinder/detail/PeerfinderConfig.cpp | 3 +- src/xrpld/rpc/detail/Pathfinder.cpp | 84 +- src/xrpld/rpc/detail/ServerHandler.cpp | 18 +- 35 files changed, 1392 insertions(+), 528 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index b23d7ccbff..2d72eae701 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -154,7 +154,7 @@ Checks: "-*, " # --- # readability-inconsistent-declaration-parameter-name, # in this codebase this check will break a lot of arg names -# readability-static-accessed-through-instance, # this check is probably unnecessary. it makes the code less readable +# readability-static-accessed-through-instance, # this check is probably unnecessary. It makes the code less readable # --- CheckOptions: diff --git a/.github/workflows/reusable-clang-tidy.yml b/.github/workflows/reusable-clang-tidy.yml index 8be1db5fb2..cfbd8af963 100644 --- a/.github/workflows/reusable-clang-tidy.yml +++ b/.github/workflows/reusable-clang-tidy.yml @@ -36,7 +36,7 @@ jobs: needs: [determine-files] if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }} runs-on: ["self-hosted", "Linux", "X64", "heavy"] - container: "ghcr.io/xrplf/ci/debian-trixie:clang-21-sha-53033a2" + container: "ghcr.io/xrplf/xrpld/nix-debian:sha-8abe82e" permissions: contents: read issues: write @@ -56,6 +56,11 @@ jobs: uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf id: nproc + - name: Set compiler environment + uses: ./.github/actions/set-compiler-env + with: + compiler: clang + - name: Setup Conan uses: ./.github/actions/setup-conan diff --git a/include/xrpl/nodestore/detail/varint.h b/include/xrpl/nodestore/detail/varint.h index e6b78fcf08..0c49274d70 100644 --- a/include/xrpl/nodestore/detail/varint.h +++ b/include/xrpl/nodestore/detail/varint.h @@ -25,7 +25,7 @@ struct varint_traits { explicit varint_traits() = default; - static constexpr std::size_t kMax = (8 * sizeof(T) + 6) / 7; + static constexpr std::size_t kMax = ((8 * sizeof(T)) + 6) / 7; }; // Returns: Number of bytes consumed or 0 on error, diff --git a/src/libxrpl/basics/Log.cpp b/src/libxrpl/basics/Log.cpp index 1079f91280..d1e54a515f 100644 --- a/src/libxrpl/basics/Log.cpp +++ b/src/libxrpl/basics/Log.cpp @@ -61,7 +61,8 @@ Logs::File::open(boost::filesystem::path const& path) bool wasOpened = false; // VFALCO TODO Make this work with Unicode file paths - std::unique_ptr stream(new std::ofstream(path.c_str(), std::fstream::app)); + std::unique_ptr stream = + std::make_unique(path.c_str(), std::fstream::app); if (stream->good()) { diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 275d82d8c9..23e913bbdc 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -1241,9 +1241,11 @@ root(Number f, unsigned d) } // Quadratic least squares curve fit of f^(1/d) in the range [0, 1] - auto const D = (((6 * di + 11) * di + 6) * di) + 1; // NOLINT(readability-identifier-naming) - auto const a0 = 3 * di * ((2 * di - 3) * di + 1); - auto const a1 = 24 * di * (2 * di - 1); + + // NOLINTNEXTLINE(readability-identifier-naming) + auto const D = (((((6 * di) + 11) * di) + 6) * di) + 1; + auto const a0 = 3 * di * ((((2 * di) - 3) * di) + 1); + auto const a1 = 24 * di * ((2 * di) - 1); auto const a2 = -30 * (di - 1) * di; Number r = ((Number{a2} * f + Number{a1}) * f + Number{a0}) / Number{D}; if (neg) diff --git a/src/libxrpl/beast/core/CurrentThreadName.cpp b/src/libxrpl/beast/core/CurrentThreadName.cpp index 52d9063179..628fec5b7a 100644 --- a/src/libxrpl/beast/core/CurrentThreadName.cpp +++ b/src/libxrpl/beast/core/CurrentThreadName.cpp @@ -71,7 +71,6 @@ setCurrentThreadNameImpl(std::string_view name) #if BOOST_OS_LINUX #include -#include #include // IWYU pragma: keep namespace beast::detail { diff --git a/src/libxrpl/server/Port.cpp b/src/libxrpl/server/Port.cpp index 00c10b2b55..b3fd7a1526 100644 --- a/src/libxrpl/server/Port.cpp +++ b/src/libxrpl/server/Port.cpp @@ -26,8 +26,8 @@ namespace xrpl { bool Port::secure() const { - return protocol.count("peer") > 0 || protocol.count("https") > 0 || protocol.count("wss") > 0 || - protocol.count("wss2") > 0; + return protocol.contains("peer") || protocol.contains("https") || protocol.contains("wss") || + protocol.contains("wss2"); } std::string diff --git a/src/test/app/AccountDelete_test.cpp b/src/test/app/AccountDelete_test.cpp index 951f99919b..65ff9ed839 100644 --- a/src/test/app/AccountDelete_test.cpp +++ b/src/test/app/AccountDelete_test.cpp @@ -813,7 +813,7 @@ public: env.close(); // alice create DepositPreauth Object - env(deposit::authCredentials(alice, {{carol, credType}})); + env(deposit::authCredentials(alice, {{.issuer = carol, .credType = credType}})); env.close(); // becky attempts to delete her account, but alice won't take her diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index 8755fe9f9c..791bb5a4d6 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -1017,7 +1017,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1059,7 +1063,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1101,7 +1109,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1143,7 +1155,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1185,7 +1201,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -1520,9 +1540,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1552,7 +1584,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1581,7 +1617,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1610,7 +1650,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); @@ -1662,10 +1706,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tecUNFUNDED_PAYMENT", txIDs[0], batchID}, - {2, "Payment", "tecUNFUNDED_PAYMENT", txIDs[1], batchID}, - {3, "Payment", "tecUNFUNDED_PAYMENT", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1695,9 +1755,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tecUNFUNDED_PAYMENT", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1727,8 +1799,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1758,8 +1838,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1789,8 +1877,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1826,11 +1922,31 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "OfferCreate", "tecKILLED", txIDs[0], batchID}, - {2, "OfferCreate", "tecKILLED", txIDs[1], batchID}, - {3, "OfferCreate", "tecKILLED", txIDs[2], batchID}, - {4, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[2], + .batchID = batchID}, + {.index = 4, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1878,8 +1994,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tecUNFUNDED_PAYMENT", txIDs[0], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[0], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1909,11 +2033,31 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[2], batchID}, - {4, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[2], + .batchID = batchID}, + {.index = 4, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1944,10 +2088,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tecUNFUNDED_PAYMENT", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -1978,9 +2138,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2011,9 +2183,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2044,10 +2228,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "OfferCreate", "tecKILLED", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2095,11 +2295,31 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tecUNFUNDED_PAYMENT", txIDs[1], batchID}, - {3, "Payment", "tecUNFUNDED_PAYMENT", txIDs[2], batchID}, - {4, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[2], + .batchID = batchID}, + {.index = 4, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2130,11 +2350,31 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tecUNFUNDED_PAYMENT", txIDs[2], batchID}, - {4, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tecUNFUNDED_PAYMENT", + .txHash = txIDs[2], + .batchID = batchID}, + {.index = 4, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2165,10 +2405,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2199,10 +2455,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[3], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[3], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2232,10 +2504,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "OfferCreate", "tecKILLED", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "OfferCreate", + .result = "tecKILLED", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2454,9 +2742,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "AccountSet", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2503,9 +2803,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "AccountSet", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2558,9 +2870,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "AccountDelete", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "AccountDelete", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2601,10 +2925,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "AccountDelete", "tecHAS_OBLIGATIONS", txIDs[1], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "AccountDelete", + .result = "tecHAS_OBLIGATIONS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2642,7 +2982,11 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); @@ -2876,9 +3220,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "CheckCash", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2922,9 +3278,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tecDST_TAG_NEEDED", txIDs[0], batchID}, - {2, "CheckCash", "tecNO_ENTRY", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tecDST_TAG_NEEDED", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "CheckCash", + .result = "tecNO_ENTRY", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -2987,10 +3355,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "TicketCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "CheckCreate", "tesSUCCESS", txIDs[1], batchID}, - {3, "CheckCash", "tesSUCCESS", txIDs[2], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "TicketCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = txIDs[2], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3047,9 +3431,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "CheckCash", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3099,9 +3495,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3147,9 +3555,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3196,9 +3616,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3257,9 +3689,21 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3268,7 +3712,11 @@ class Batch_test : public beast::unit_test::Suite { // next ledger contains noop txn std::vector const testCases = { - {0, "AccountSet", "tesSUCCESS", noopTxnID, std::nullopt}, + {.index = 0, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = noopTxnID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -3301,9 +3749,21 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3340,9 +3800,21 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3382,10 +3854,26 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "AccountSet", "tesSUCCESS", noopTxnID, std::nullopt}, - {1, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {2, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = noopTxnID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3442,9 +3930,21 @@ class Batch_test : public beast::unit_test::Suite { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3489,9 +3989,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3552,10 +4064,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "CheckCash", "tesSUCCESS", objTxnID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = objTxnID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -3601,10 +4129,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "CheckCreate", "tesSUCCESS", objTxnID, std::nullopt}, - {1, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {2, "CheckCash", "tesSUCCESS", txIDs[0], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = objTxnID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 2, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -3646,10 +4190,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); { std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "CheckCreate", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, - {3, "CheckCash", "tesSUCCESS", objTxnID, std::nullopt}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "CheckCreate", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, + {.index = 3, + .txType = "CheckCash", + .result = "tesSUCCESS", + .txHash = objTxnID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -3742,10 +4302,26 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Payment", "tesSUCCESS", payTxn1ID, std::nullopt}, - {1, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {2, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {3, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = payTxn1ID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 3, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -3753,7 +4329,11 @@ class Batch_test : public beast::unit_test::Suite { // next ledger includes the payment txn std::vector const testCases = { - {0, "Payment", "tesSUCCESS", payTxn2ID, std::nullopt}, + {.index = 0, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = payTxn2ID, + .batchID = std::nullopt}, }; validateClosedLedger(env, testCases); } @@ -3965,9 +4545,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -4014,9 +4606,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "Payment", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -4064,9 +4668,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "AccountSet", "tesSUCCESS", txIDs[0], batchID}, - {2, "Payment", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "AccountSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "Payment", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); @@ -4126,9 +4742,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "MPTokenIssuanceSet", "tesSUCCESS", txIDs[0], batchID}, - {2, "MPTokenIssuanceSet", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "MPTokenIssuanceSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "MPTokenIssuanceSet", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -4167,9 +4795,21 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "TrustSet", "tesSUCCESS", txIDs[0], batchID}, - {2, "TrustSet", "tesSUCCESS", txIDs[1], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "TrustSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, + {.index = 2, + .txType = "TrustSet", + .result = "tesSUCCESS", + .txHash = txIDs[1], + .batchID = batchID}, }; validateClosedLedger(env, testCases); } @@ -4207,8 +4847,16 @@ class Batch_test : public beast::unit_test::Suite env.close(); std::vector const testCases = { - {0, "Batch", "tesSUCCESS", batchID, std::nullopt}, - {1, "TrustSet", "tesSUCCESS", txIDs[0], batchID}, + {.index = 0, + .txType = "Batch", + .result = "tesSUCCESS", + .txHash = batchID, + .batchID = std::nullopt}, + {.index = 1, + .txType = "TrustSet", + .result = "tesSUCCESS", + .txHash = txIDs[0], + .batchID = batchID}, // jv2 fails with terNO_DELEGATE_PERMISSION. }; validateClosedLedger(env, testCases); diff --git a/src/test/app/Credentials_test.cpp b/src/test/app/Credentials_test.cpp index 9416ca222f..456a53bc01 100644 --- a/src/test/app/Credentials_test.cpp +++ b/src/test/app/Credentials_test.cpp @@ -1078,7 +1078,7 @@ struct Credentials_test : public beast::unit_test::Suite } // Create DepositPreauth - env(deposit::authCredentials(becky, {{subject, credType}})); + env(deposit::authCredentials(becky, {{.issuer = subject, .credType = credType}})); env.close(); // env(); auto jtx = env.jt(pay(subject, becky, XRP(100)), credentials::Ids({credIdx})); @@ -1087,7 +1087,7 @@ struct Credentials_test : public beast::unit_test::Suite auto const stx = std::make_shared(*jtx.stx); // Create PermissionedDomain - env(pdomain::setTx(becky, {{issuer, credType}})); + env(pdomain::setTx(becky, {{.issuer = issuer, .credType = credType}})); env.close(); auto const objects = pdomain::getObjects(becky, env); if (!BEAST_EXPECT(!objects.empty())) diff --git a/src/test/app/CrossingLimitsMPT_test.cpp b/src/test/app/CrossingLimitsMPT_test.cpp index 4a016f31dc..8bd0c767f7 100644 --- a/src/test/app/CrossingLimitsMPT_test.cpp +++ b/src/test/app/CrossingLimitsMPT_test.cpp @@ -270,7 +270,7 @@ public: env.require(Balance(alice, usd(2'503))); env.require(Balance(alice, eur(1'100))); - auto const numAOffers = 2'000 + 100 + 1'000 + 1 - (2 * 100 + 2 * 199 + 1 + 1); + auto const numAOffers = 2'000 + 100 + 1'000 + 1 - ((2 * 100) + (2 * 199) + 1 + 1); env.require(offers(alice, numAOffers)); env.require(Owners(alice, numAOffers + 2)); @@ -358,7 +358,7 @@ public: env.require(Balance(alice, usd(2'494))); env.require(Balance(alice, eur(1'100))); auto const numAOffers = - 1 + 2'000 + 100 + 1'000 + 1 - (1 + 2 * 100 + 2 * 199 + 1 + 1); + 1 + 2'000 + 100 + 1'000 + 1 - (1 + (2 * 100) + (2 * 199) + 1 + 1); env.require(offers(alice, numAOffers)); env.require(Owners(alice, numAOffers + 2)); diff --git a/src/test/app/CrossingLimits_test.cpp b/src/test/app/CrossingLimits_test.cpp index 3cf8f50990..c48892f04e 100644 --- a/src/test/app/CrossingLimits_test.cpp +++ b/src/test/app/CrossingLimits_test.cpp @@ -258,7 +258,7 @@ public: env.require(Balance(alice, usd(2503))); env.require(Balance(alice, eur(1100))); - auto const numAOffers = 2000 + 100 + 1000 + 1 - (2 * 100 + 2 * 199 + 1 + 1); + auto const numAOffers = 2000 + 100 + 1000 + 1 - ((2 * 100) + (2 * 199) + 1 + 1); env.require(offers(alice, numAOffers)); env.require(Owners(alice, numAOffers + 2)); @@ -329,7 +329,7 @@ public: env.require(Balance(alice, usd(2494))); env.require(Balance(alice, eur(1100))); - auto const numAOffers = 1 + 2000 + 100 + 1000 + 1 - (1 + 2 * 100 + 2 * 199 + 1 + 1); + auto const numAOffers = 1 + 2000 + 100 + 1000 + 1 - (1 + (2 * 100) + (2 * 199) + 1 + 1); env.require(offers(alice, numAOffers)); env.require(Owners(alice, numAOffers + 2)); diff --git a/src/test/app/DepositAuth_test.cpp b/src/test/app/DepositAuth_test.cpp index 98597a175f..3496e67b54 100644 --- a/src/test/app/DepositAuth_test.cpp +++ b/src/test/app/DepositAuth_test.cpp @@ -614,7 +614,8 @@ struct DepositPreauth_test : public beast::unit_test::Suite TER const expectTer(!supportsCredentials ? TER(temDISABLED) : TER(tesSUCCESS)); - env(deposit::authCredentials(becky, {{carol, credType}}), Ter(expectTer)); + env(deposit::authCredentials(becky, {{.issuer = carol, .credType = credType}}), + Ter(expectTer)); env.close(); // gw accept credentials @@ -744,7 +745,8 @@ struct DepositPreauth_test : public beast::unit_test::Suite env.close(); // Setup DepositPreauth object failed - amendent is not supported - env(deposit::authCredentials(bob, {{issuer, credType}}), Ter(temDISABLED)); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}), + Ter(temDISABLED)); env.close(); // But can create old DepositPreauth @@ -782,10 +784,11 @@ struct DepositPreauth_test : public beast::unit_test::Suite // Bob will accept payments from accounts with credentials signed // by 'issuer' - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); - auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}}); + auto const jDP = + ledgerEntryDepositPreauth(env, bob, {{.issuer = issuer, .credType = credType}}); BEAST_EXPECT( jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) && jDP[jss::result].isMember(jss::node) && @@ -858,11 +861,14 @@ struct DepositPreauth_test : public beast::unit_test::Suite } // Bob setup DepositPreauth object, duplicates is not allowed - env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType}}), + env(deposit::authCredentials( + bob, + {{.issuer = issuer, .credType = credType}, + {.issuer = issuer, .credType = credType}}), Ter(temMALFORMED)); // Bob setup DepositPreauth object - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); { @@ -928,35 +934,37 @@ struct DepositPreauth_test : public beast::unit_test::Suite { // both included [AuthorizeCredentials UnauthorizeCredentials] - auto jv = deposit::authCredentials(bob, {{issuer, credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfUnauthorizeCredentials.jsonName] = json::ValueType::Array; env(jv, Ter(temMALFORMED)); } { // both included [Unauthorize, AuthorizeCredentials] - auto jv = deposit::authCredentials(bob, {{issuer, credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfUnauthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } { // both included [Authorize, AuthorizeCredentials] - auto jv = deposit::authCredentials(bob, {{issuer, credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfAuthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } { // both included [Unauthorize, UnauthorizeCredentials] - auto jv = deposit::unauthCredentials(bob, {{issuer, credType}}); + auto jv = + deposit::unauthCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfUnauthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } { // both included [Authorize, UnauthorizeCredentials] - auto jv = deposit::unauthCredentials(bob, {{issuer, credType}}); + auto jv = + deposit::unauthCredentials(bob, {{.issuer = issuer, .credType = credType}}); jv[sfAuthorize.jsonName] = issuer.human(); env(jv, Ter(temMALFORMED)); } @@ -983,7 +991,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite { // empty credential type - auto jv = deposit::authCredentials(bob, {{issuer, {}}}); + auto jv = deposit::authCredentials(bob, {{.issuer = issuer, .credType = {}}}); env(jv, Ter(temMALFORMED)); } @@ -993,14 +1001,23 @@ struct DepositPreauth_test : public beast::unit_test::Suite i("i"); auto const& z = credType; auto jv = deposit::authCredentials( - bob, {{a, z}, {b, z}, {c, z}, {d, z}, {e, z}, {f, z}, {g, z}, {h, z}, {i, z}}); + bob, + {{.issuer = a, .credType = z}, + {.issuer = b, .credType = z}, + {.issuer = c, .credType = z}, + {.issuer = d, .credType = z}, + {.issuer = e, .credType = z}, + {.issuer = f, .credType = z}, + {.issuer = g, .credType = z}, + {.issuer = h, .credType = z}, + {.issuer = i, .credType = z}}); env(jv, Ter(temARRAY_TOO_LARGE)); } { // Can't create with non-existing issuer Account const rick{"rick"}; - auto jv = deposit::authCredentials(bob, {{rick, credType}}); + auto jv = deposit::authCredentials(bob, {{.issuer = rick, .credType = credType}}); env(jv, Ter(tecNO_ISSUER)); env.close(); } @@ -1010,21 +1027,24 @@ struct DepositPreauth_test : public beast::unit_test::Suite Account const john{"john"}; env.fund(env.current()->fees().accountReserve(0), john); env.close(); - auto jv = deposit::authCredentials(john, {{issuer, credType}}); + auto jv = + deposit::authCredentials(john, {{.issuer = issuer, .credType = credType}}); env(jv, Ter(tecINSUFFICIENT_RESERVE)); } { // NO deposit object exists - env(deposit::unauthCredentials(bob, {{issuer, credType}}), Ter(tecNO_ENTRY)); + env(deposit::unauthCredentials(bob, {{.issuer = issuer, .credType = credType}}), + Ter(tecNO_ENTRY)); } // Create DepositPreauth object { - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); - auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}}); + auto const jDP = + ledgerEntryDepositPreauth(env, bob, {{.issuer = issuer, .credType = credType}}); BEAST_EXPECT( jDP.isObject() && jDP.isMember(jss::result) && !jDP[jss::result].isMember(jss::error) && @@ -1045,14 +1065,16 @@ struct DepositPreauth_test : public beast::unit_test::Suite } // can't create duplicate - env(deposit::authCredentials(bob, {{issuer, credType}}), Ter(tecDUPLICATE)); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}}), + Ter(tecDUPLICATE)); } // Delete DepositPreauth object { - env(deposit::unauthCredentials(bob, {{issuer, credType}})); + env(deposit::unauthCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); - auto const jDP = ledgerEntryDepositPreauth(env, bob, {{issuer, credType}}); + auto const jDP = + ledgerEntryDepositPreauth(env, bob, {{.issuer = issuer, .credType = credType}}); BEAST_EXPECT( jDP.isObject() && jDP.isMember(jss::result) && jDP[jss::result].isMember(jss::error) && @@ -1119,7 +1141,10 @@ struct DepositPreauth_test : public beast::unit_test::Suite env(fset(bob, asfDepositAuth)); env.close(); // Bob setup DepositPreauth object - env(deposit::authCredentials(bob, {{issuer, credType}, {issuer, credType2}})); + env(deposit::authCredentials( + bob, + {{.issuer = issuer, .credType = credType}, + {.issuer = issuer, .credType = credType2}})); env.close(); { @@ -1228,7 +1253,7 @@ struct DepositPreauth_test : public beast::unit_test::Suite env(fset(bob, asfDepositAuth)); env.close(); // Bob setup DepositPreauth object - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); auto const seq = env.seq(alice); @@ -1286,14 +1311,14 @@ struct DepositPreauth_test : public beast::unit_test::Suite env.fund(XRP(5000), stock, alice, bob); std::vector credentials = { - {"a", "a"}, - {"b", "b"}, - {"c", "c"}, - {"d", "d"}, - {"e", "e"}, - {"f", "f"}, - {"g", "g"}, - {"h", "h"}}; + {.issuer = "a", .credType = "a"}, + {.issuer = "b", .credType = "b"}, + {.issuer = "c", .credType = "c"}, + {.issuer = "d", .credType = "d"}, + {.issuer = "e", .credType = "e"}, + {.issuer = "f", .credType = "f"}, + {.issuer = "g", .credType = "g"}, + {.issuer = "h", .credType = "h"}}; for (auto const& c : credentials) env.fund(XRP(5000), c.issuer); diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 3e76524cf1..5623bc4443 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -1544,7 +1544,7 @@ struct Escrow_test : public beast::unit_test::Suite credentials::Ids({credIdx}), Ter(tecNO_PERMISSION)); - env(deposit::authCredentials(bob, {{zelda, credType}})); + env(deposit::authCredentials(bob, {{.issuer = zelda, .credType = credType}})); env.close(); // Success @@ -1601,7 +1601,7 @@ struct Escrow_test : public beast::unit_test::Suite // Bob require pre-authorization env(fset(bob, asfDepositAuth)); env.close(); - env(deposit::authCredentials(bob, {{zelda, credType}})); + env(deposit::authCredentials(bob, {{.issuer = zelda, .credType = credType}})); env.close(); // Use any valid credentials if account == dst diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index 1978d04fe1..810d93e6e1 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -549,7 +549,7 @@ struct LedgerServer while (senders.contains(fromIdx)) fromIdx = (fromIdx + 1) % fundedAccounts; senders.insert(fromIdx); - toIdx = (toIdx + r * 2) % fundedAccounts; + toIdx = (toIdx + (r * 2)) % fundedAccounts; if (toIdx == fromIdx) toIdx = (toIdx + 1) % fundedAccounts; }; diff --git a/src/test/app/PayStrand_test.cpp b/src/test/app/PayStrand_test.cpp index 67a37833b2..471c641f36 100644 --- a/src/test/app/PayStrand_test.cpp +++ b/src/test/app/PayStrand_test.cpp @@ -632,7 +632,13 @@ struct PayStrand_test : public beast::unit_test::Suite // Insert implied account test( - env, usd, std::nullopt, STPath(), tesSUCCESS, D{alice, gw, usdC}, D{gw, bob, usdC}); + env, + usd, + std::nullopt, + STPath(), + tesSUCCESS, + D{.src = alice, .dst = gw, .currency = usdC}, + D{.src = gw, .dst = bob, .currency = usdC}); env.trust(eur(1000), alice, bob); // Insert implied offer @@ -642,9 +648,9 @@ struct PayStrand_test : public beast::unit_test::Suite usd, STPath(), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, eur, std::nullopt}, - D{gw, bob, eurC}); + D{.src = gw, .dst = bob, .currency = eurC}); // Path with explicit offer test( @@ -653,9 +659,9 @@ struct PayStrand_test : public beast::unit_test::Suite usd, STPath({ipe(eur)}), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, eur, std::nullopt}, - D{gw, bob, eurC}); + D{.src = gw, .dst = bob, .currency = eurC}); // Path with offer that changes issuer only env.trust(carol["USD"](1000), bob); @@ -665,9 +671,9 @@ struct PayStrand_test : public beast::unit_test::Suite usd, STPath({iape(carol)}), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, carol["USD"], std::nullopt}, - D{carol, bob, usdC}); + D{.src = carol, .dst = bob, .currency = usdC}); // Path with XRP src currency test( @@ -678,7 +684,7 @@ struct PayStrand_test : public beast::unit_test::Suite tesSUCCESS, XRPS{alice}, B{XRP, usd, std::nullopt}, - D{gw, bob, usdC}); + D{.src = gw, .dst = bob, .currency = usdC}); // Path with XRP dst currency. test( @@ -688,7 +694,7 @@ struct PayStrand_test : public beast::unit_test::Suite STPath({STPathElement{ STPathElement::TypeCurrency, xrpAccount(), xrpCurrency(), xrpAccount()}}), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, XRP, std::nullopt}, XRPS{bob}); @@ -699,10 +705,10 @@ struct PayStrand_test : public beast::unit_test::Suite usd, STPath({cpe(xrpCurrency())}), tesSUCCESS, - D{alice, gw, usdC}, + D{.src = alice, .dst = gw, .currency = usdC}, B{usd, XRP, std::nullopt}, B{XRP, eur, std::nullopt}, - D{gw, bob, eurC}); + D{.src = gw, .dst = bob, .currency = eurC}); // XRP -> XRP transaction can't include a path test(env, XRP, std::nullopt, STPath({ape(carol)}), temBAD_PATH); diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index a88cbaa868..d534f20248 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -704,7 +704,8 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); auto const badCredType = "badCred"; - pdomain::Credentials const credentials{{badDomainOwner, badCredType}}; + pdomain::Credentials const credentials{ + {.issuer = badDomainOwner, .credType = badCredType}}; env(pdomain::setTx(badDomainOwner, credentials)); auto objects = pdomain::getObjects(badDomainOwner, env); @@ -1222,7 +1223,8 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); auto const badCredType = "badCred"; - pdomain::Credentials const credentials{{badDomainOwner, badCredType}}; + pdomain::Credentials const credentials{ + {.issuer = badDomainOwner, .credType = badCredType}}; env(pdomain::setTx(badDomainOwner, credentials)); auto objects = pdomain::getObjects(badDomainOwner, env); diff --git a/src/test/app/PermissionedDomains_test.cpp b/src/test/app/PermissionedDomains_test.cpp index 0857a4bdef..f2d7bce152 100644 --- a/src/test/app/PermissionedDomains_test.cpp +++ b/src/test/app/PermissionedDomains_test.cpp @@ -62,7 +62,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite Account const alice("alice"); Env env(*this, features); env.fund(XRP(1000), alice); - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials)); BEAST_EXPECT(env.ownerCount(alice) == 1); auto objects = pdomain::getObjects(alice, env); @@ -84,7 +84,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite Account const alice("alice"); Env env(*this, amendments); env.fund(XRP(1000), alice); - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials), Ter(temDISABLED)); } @@ -96,7 +96,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite Account const alice("alice"); Env env(*this, testableAmendments() - featurePermissionedDomains); env.fund(XRP(1000), alice); - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials), Ter(temDISABLED)); env(pdomain::deleteTx(alice, uint256(75)), Ter(temDISABLED)); } @@ -124,40 +124,40 @@ class PermissionedDomains_test : public beast::unit_test::Suite // Test 11 credentials. pdomain::Credentials const credentials11{ - {alice2, "credential1"}, - {alice3, "credential2"}, - {alice4, "credential3"}, - {alice5, "credential4"}, - {alice6, "credential5"}, - {alice7, "credential6"}, - {alice8, "credential7"}, - {alice9, "credential8"}, - {alice10, "credential9"}, - {alice11, "credential10"}, - {alice12, "credential11"}}; + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice4, .credType = "credential3"}, + {.issuer = alice5, .credType = "credential4"}, + {.issuer = alice6, .credType = "credential5"}, + {.issuer = alice7, .credType = "credential6"}, + {.issuer = alice8, .credType = "credential7"}, + {.issuer = alice9, .credType = "credential8"}, + {.issuer = alice10, .credType = "credential9"}, + {.issuer = alice11, .credType = "credential10"}, + {.issuer = alice12, .credType = "credential11"}}; BEAST_EXPECT(credentials11.size() == kMaxPermissionedDomainCredentialsArraySize + 1); env(pdomain::setTx(account, credentials11, domain), Ter(temARRAY_TOO_LARGE)); // Test credentials including non-existent issuer. Account const nobody("nobody"); pdomain::Credentials const credentialsNon{ - {alice2, "credential1"}, - {alice3, "credential2"}, - {alice4, "credential3"}, - {nobody, "credential4"}, - {alice5, "credential5"}, - {alice6, "credential6"}, - {alice7, "credential7"}}; + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice4, .credType = "credential3"}, + {.issuer = nobody, .credType = "credential4"}, + {.issuer = alice5, .credType = "credential5"}, + {.issuer = alice6, .credType = "credential6"}, + {.issuer = alice7, .credType = "credential7"}}; env(pdomain::setTx(account, credentialsNon, domain), Ter(tecNO_ISSUER)); // Test bad fee env(pdomain::setTx(account, credentials11, domain), Fee(1, true), Ter(temBAD_FEE)); pdomain::Credentials const credentials4{ - {alice2, "credential1"}, - {alice3, "credential2"}, - {alice4, "credential3"}, - {alice5, "credential4"}, + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice4, .credType = "credential3"}, + {.issuer = alice5, .credType = "credential4"}, }; auto txJsonMutable = pdomain::setTx(account, credentials4, domain); auto const credentialOrig = txJsonMutable["AcceptedCredentials"][2u]; @@ -192,11 +192,11 @@ class PermissionedDomains_test : public beast::unit_test::Suite // permissioned domains, so transactions should return errors { pdomain::Credentials const credentialsDup{ - {alice7, "credential6"}, - {alice2, "credential1"}, - {alice3, "credential2"}, - {alice2, "credential1"}, - {alice5, "credential4"}, + {.issuer = alice7, .credType = "credential6"}, + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice2, .credType = "credential1"}, + {.issuer = alice5, .credType = "credential4"}, }; std::unordered_map human2Acc; @@ -230,11 +230,11 @@ class PermissionedDomains_test : public beast::unit_test::Suite // sort correctly. { pdomain::Credentials const credentialsSame{ - {alice2, "credential3"}, - {alice3, "credential2"}, - {alice2, "credential9"}, - {alice5, "credential4"}, - {alice2, "credential6"}, + {.issuer = alice2, .credType = "credential3"}, + {.issuer = alice3, .credType = "credential2"}, + {.issuer = alice2, .credType = "credential9"}, + {.issuer = alice5, .credType = "credential4"}, + {.issuer = alice2, .credType = "credential6"}, }; std::unordered_map human2Acc; for (auto const& c : credentialsSame) @@ -290,7 +290,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite env.fund(XRP(1000), alice[i]); // Create new from existing account with a single credential. - pdomain::Credentials const credentials1{{alice[2], "credential1"}}; + pdomain::Credentials const credentials1{{.issuer = alice[2], .credType = "credential1"}}; { env(pdomain::setTx(alice[0], credentials1)); BEAST_EXPECT(env.ownerCount(alice[0]) == 1); @@ -314,7 +314,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite "89"; static_assert(kLongCredentialType.size() == kMaxCredentialTypeLength); pdomain::Credentials const longCredentials{ - {alice[1], std::string(kLongCredentialType)}}; + {.issuer = alice[1], .credType = std::string(kLongCredentialType)}}; env(pdomain::setTx(alice[0], longCredentials)); @@ -345,16 +345,16 @@ class PermissionedDomains_test : public beast::unit_test::Suite // Create new from existing account with 10 credentials. // Last credential describe domain owner itself pdomain::Credentials const credentials10{ - {alice[2], "credential1"}, - {alice[3], "credential2"}, - {alice[4], "credential3"}, - {alice[5], "credential4"}, - {alice[6], "credential5"}, - {alice[7], "credential6"}, - {alice[8], "credential7"}, - {alice[9], "credential8"}, - {alice[10], "credential9"}, - {alice[0], "credential10"}, + {.issuer = alice[2], .credType = "credential1"}, + {.issuer = alice[3], .credType = "credential2"}, + {.issuer = alice[4], .credType = "credential3"}, + {.issuer = alice[5], .credType = "credential4"}, + {.issuer = alice[6], .credType = "credential5"}, + {.issuer = alice[7], .credType = "credential6"}, + {.issuer = alice[8], .credType = "credential7"}, + {.issuer = alice[9], .credType = "credential8"}, + {.issuer = alice[10], .credType = "credential9"}, + {.issuer = alice[0], .credType = "credential10"}, }; uint256 domain2; { @@ -434,7 +434,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite env.fund(XRP(1000), alice); auto const setFee(drops(env.current()->fees().increment)); - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials)); env.close(); @@ -498,7 +498,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite BEAST_EXPECT(env.ownerCount(alice) == 0); // alice does not have enough XRP to cover the reserve. - pdomain::Credentials const credentials{{alice, "first credential"}}; + pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; env(pdomain::setTx(alice, credentials), Ter(tecINSUFFICIENT_RESERVE)); BEAST_EXPECT(env.ownerCount(alice) == 0); BEAST_EXPECT(pdomain::getObjects(alice, env).empty()); diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index 8333fce3b3..dc28388de4 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -1228,7 +1228,7 @@ public: // Try to replace a middle item in the queue // with enough fee to bankrupt bob and make the // later transactions unable to pay their fees - std::int64_t bobFee = env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10 - 1); + std::int64_t bobFee = env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - ((9 * 10) - 1); env(noop(bob), Seq(bobSeq + 5), Fee(bobFee), Ter(telCAN_NOT_QUEUE_BALANCE)); checkMetrics(*this, env, 10, 12, 7, 6); diff --git a/src/test/app/ValidatorList_test.cpp b/src/test/app/ValidatorList_test.cpp index 20a3557db5..80483446a2 100644 --- a/src/test/app/ValidatorList_test.cpp +++ b/src/test/app/ValidatorList_test.cpp @@ -632,7 +632,11 @@ private: checkResult( trustedKeys->applyLists( - manifest1, version, {{expiredblob, expiredSig, {}}, {blob2, sig2, {}}}, siteUri), + manifest1, + version, + {{.blob = expiredblob, .signature = expiredSig, .manifest = {}}, + {.blob = blob2, .signature = sig2, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Expired, ListDisposition::Accepted); @@ -665,7 +669,11 @@ private: checkResult( trustedKeys->applyLists( - manifest1, version2, {{blob7, sig7, {}}, {blob8, sig8, {}}}, siteUri), + manifest1, + version2, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob8, .signature = sig8, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Pending, ListDisposition::Pending); @@ -697,7 +705,11 @@ private: checkResult( trustedKeys->applyLists( - manifest1, version, {{blob6a, sig6a, {}}, {blob6, sig6, {}}}, siteUri), + manifest1, + version, + {{.blob = blob6a, .signature = sig6a, .manifest = {}}, + {.blob = blob6, .signature = sig6, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Pending, ListDisposition::Pending); @@ -709,7 +721,11 @@ private: checkResult( trustedKeys->applyLists( - manifest1, version, {{blob7, sig7, {}}, {blob6, sig6, {}}}, siteUri), + manifest1, + version, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob6, .signature = sig6, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::KnownSequence, ListDisposition::KnownSequence); @@ -720,7 +736,12 @@ private: // try empty or mangled manifest checkResult( - trustedKeys->applyLists("", version, {{blob7, sig7, {}}, {blob6, sig6, {}}}, siteUri), + trustedKeys->applyLists( + "", + version, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob6, .signature = sig6, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Invalid, ListDisposition::Invalid); @@ -729,7 +750,8 @@ private: trustedKeys->applyLists( base64Encode("not a manifest"), version, - {{blob7, sig7, {}}, {blob6, sig6, {}}}, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob6, .signature = sig6, .manifest = {}}}, siteUri), publisherPublic, ListDisposition::Invalid, @@ -740,7 +762,11 @@ private: randomMasterKey(), publisherSecret, pubSigningKeys1.first, pubSigningKeys1.second, 1)); checkResult( - trustedKeys->applyLists(untrustedManifest, version, {{blob2, sig2, {}}}, siteUri), + trustedKeys->applyLists( + untrustedManifest, + version, + {{.blob = blob2, .signature = sig2, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Untrusted, ListDisposition::Untrusted); @@ -748,7 +774,11 @@ private: // do not use list with unhandled version auto const badVersion = 666; checkResult( - trustedKeys->applyLists(manifest1, badVersion, {{blob2, sig2, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, + badVersion, + {{.blob = blob2, .signature = sig2, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::UnsupportedVersion, ListDisposition::UnsupportedVersion); @@ -759,7 +789,8 @@ private: auto const sig3 = signList(blob3, pubSigningKeys1); checkResult( - trustedKeys->applyLists(manifest1, version, {{blob3, sig3, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, version, {{.blob = blob3, .signature = sig3, .manifest = {}}}, siteUri), publisherPublic, ListDisposition::Accepted, ListDisposition::Accepted); @@ -780,7 +811,11 @@ private: // do not re-apply lists with past or current sequence numbers checkResult( trustedKeys->applyLists( - manifest1, version, {{blob2, sig2, {}}, {blob3, sig3, {}}}, siteUri), + manifest1, + version, + {{.blob = blob2, .signature = sig2, .manifest = {}}, + {.blob = blob3, .signature = sig3, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Stale, ListDisposition::SameSequence); @@ -799,7 +834,9 @@ private: trustedKeys->applyLists( manifest2, version, - {{blob2, sig2, manifest1}, {blob3, sig3, manifest1}, {blob4, sig4, {}}}, + {{.blob = blob2, .signature = sig2, .manifest = manifest1}, + {.blob = blob3, .signature = sig3, .manifest = manifest1}, + {.blob = blob4, .signature = sig4, .manifest = {}}}, siteUri), publisherPublic, ListDisposition::Stale, @@ -820,7 +857,11 @@ private: auto const blob5 = makeList(lists.at(5), sequence5, validUntil.time_since_epoch().count()); auto const badSig = signList(blob5, pubSigningKeys1); checkResult( - trustedKeys->applyLists(manifest1, version, {{blob5, badSig, {}}}, siteUri), + trustedKeys->applyLists( + manifest1, + version, + {{.blob = blob5, .signature = badSig, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Invalid, ListDisposition::Invalid); @@ -833,7 +874,11 @@ private: // Reprocess the pending list, but the signature is no longer valid checkResult( trustedKeys->applyLists( - manifest1, version, {{blob7, sig7, {}}, {blob8, sig8, {}}}, siteUri), + manifest1, + version, + {{.blob = blob7, .signature = sig7, .manifest = {}}, + {.blob = blob8, .signature = sig8, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Invalid, ListDisposition::Invalid); @@ -884,7 +929,11 @@ private: checkResult( trustedKeys->applyLists( - manifest2, version, {{blob8, sig8, manifest1}, {blob8, sig82, {}}}, siteUri), + manifest2, + version, + {{.blob = blob8, .signature = sig8, .manifest = manifest1}, + {.blob = blob8, .signature = sig82, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Invalid, ListDisposition::SameSequence); @@ -903,7 +952,11 @@ private: auto const sig9 = signList(blob9, signingKeysMax); checkResult( - trustedKeys->applyLists(maxManifest, version, {{blob9, sig9, {}}}, siteUri), + trustedKeys->applyLists( + maxManifest, + version, + {{.blob = blob9, .signature = sig9, .manifest = {}}}, + siteUri), publisherPublic, ListDisposition::Untrusted, ListDisposition::Untrusted); @@ -1900,7 +1953,9 @@ private: return PreparedList{ .publisherPublic = publisherPublic, .manifest = manifest, - .blobs = {{blob1, sig1, {}}, {blob2, sig2, {}}}, + .blobs = + {{.blob = blob1, .signature = sig1, .manifest = {}}, + {.blob = blob2, .signature = sig2, .manifest = {}}}, .version = version, .expirations = {expiration1, expiration2}}; }; diff --git a/src/test/app/ValidatorSite_test.cpp b/src/test/app/ValidatorSite_test.cpp index f7f805faa2..e3e3ec27df 100644 --- a/src/test/app/ValidatorSite_test.cpp +++ b/src/test/app/ValidatorSite_test.cpp @@ -380,226 +380,333 @@ public: for (auto ssl : {true, false}) { // fetch single site - testFetchList(good, {{"/validators", "", ssl}}); - testFetchList(good, {{"/validators2", "", ssl}}); + testFetchList(good, {{.path = "/validators", .msg = "", .ssl = ssl}}); + testFetchList(good, {{.path = "/validators2", .msg = "", .ssl = ssl}}); // fetch multiple sites - testFetchList(good, {{"/validators", "", ssl}, {"/validators", "", ssl}}); - testFetchList(good, {{"/validators", "", ssl}, {"/validators2", "", ssl}}); - testFetchList(good, {{"/validators2", "", ssl}, {"/validators", "", ssl}}); - testFetchList(good, {{"/validators2", "", ssl}, {"/validators2", "", ssl}}); + testFetchList( + good, + {{.path = "/validators", .msg = "", .ssl = ssl}, + {.path = "/validators", .msg = "", .ssl = ssl}}); + testFetchList( + good, + {{.path = "/validators", .msg = "", .ssl = ssl}, + {.path = "/validators2", .msg = "", .ssl = ssl}}); + testFetchList( + good, + {{.path = "/validators2", .msg = "", .ssl = ssl}, + {.path = "/validators", .msg = "", .ssl = ssl}}); + testFetchList( + good, + {{.path = "/validators2", .msg = "", .ssl = ssl}, + {.path = "/validators2", .msg = "", .ssl = ssl}}); // fetch single site with single redirects - testFetchList(good, {{"/redirect_once/301", "", ssl}}); - testFetchList(good, {{"/redirect_once/302", "", ssl}}); - testFetchList(good, {{"/redirect_once/307", "", ssl}}); - testFetchList(good, {{"/redirect_once/308", "", ssl}}); + testFetchList(good, {{.path = "/redirect_once/301", .msg = "", .ssl = ssl}}); + testFetchList(good, {{.path = "/redirect_once/302", .msg = "", .ssl = ssl}}); + testFetchList(good, {{.path = "/redirect_once/307", .msg = "", .ssl = ssl}}); + testFetchList(good, {{.path = "/redirect_once/308", .msg = "", .ssl = ssl}}); // one redirect, one not - testFetchList(good, {{"/validators", "", ssl}, {"/redirect_once/302", "", ssl}}); - testFetchList(good, {{"/validators2", "", ssl}, {"/redirect_once/302", "", ssl}}); + testFetchList( + good, + {{.path = "/validators", .msg = "", .ssl = ssl}, + {.path = "/redirect_once/302", .msg = "", .ssl = ssl}}); + testFetchList( + good, + {{.path = "/validators2", .msg = "", .ssl = ssl}, + {.path = "/redirect_once/302", .msg = "", .ssl = ssl}}); // UNLs with a "gap" between validUntil of one and validFrom of the // next testFetchList( good, - {{"/validators2", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - std::chrono::seconds{-90}}}); + {{.path = "/validators2", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = std::chrono::seconds{-90}}}); // fetch single site with unending redirect (fails to load) testFetchList( - good, {{"/redirect_forever/301", "Exceeded max redirects", ssl, true, true}}); + good, + {{.path = "/redirect_forever/301", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // two that redirect forever testFetchList( good, - {{"/redirect_forever/307", "Exceeded max redirects", ssl, true, true}, - {"/redirect_forever/308", "Exceeded max redirects", ssl, true, true}}); + {{.path = "/redirect_forever/307", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}, + {.path = "/redirect_forever/308", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // one unending redirect, one not testFetchList( good, - {{"/validators", "", ssl}, - {"/redirect_forever/302", "Exceeded max redirects", ssl, true, true}}); + {{.path = "/validators", .msg = "", .ssl = ssl}, + {.path = "/redirect_forever/302", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // one unending redirect, one not testFetchList( good, - {{"/validators2", "", ssl}, - {"/redirect_forever/302", "Exceeded max redirects", ssl, true, true}}); + {{.path = "/validators2", .msg = "", .ssl = ssl}, + {.path = "/redirect_forever/302", + .msg = "Exceeded max redirects", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // invalid redir Location testFetchList( good, - {{"/redirect_to/ftp://invalid-url/302", - "Invalid redirect location", - ssl, - true, - true}}); + {{.path = "/redirect_to/ftp://invalid-url/302", + .msg = "Invalid redirect location", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); testFetchList( good, - {{"/redirect_to/file://invalid-url/302", - "Invalid redirect location", - ssl, - true, - true}}); + {{.path = "/redirect_to/file://invalid-url/302", + .msg = "Invalid redirect location", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // invalid json testFetchList( - good, {{"/validators/bad", "Unable to parse JSON response", ssl, true, true}}); + good, + {{.path = "/validators/bad", + .msg = "Unable to parse JSON response", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); testFetchList( - good, {{"/validators2/bad", "Unable to parse JSON response", ssl, true, true}}); + good, + {{.path = "/validators2/bad", + .msg = "Unable to parse JSON response", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // error status returned - testFetchList(good, {{"/bad-resource", "returned bad status", ssl, true, true}}); + testFetchList( + good, + {{.path = "/bad-resource", + .msg = "returned bad status", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // location field missing testFetchList( good, - {{"/redirect_nolo/308", "returned a redirect with no Location", ssl, true, true}}); + {{.path = "/redirect_nolo/308", + .msg = "returned a redirect with no Location", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // json fields missing testFetchList( good, - {{"/validators/missing", "Missing fields in JSON response", ssl, true, true}}); + {{.path = "/validators/missing", + .msg = "Missing fields in JSON response", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); testFetchList( good, - {{"/validators2/missing", "Missing fields in JSON response", ssl, true, true}}); + {{.path = "/validators2/missing", + .msg = "Missing fields in JSON response", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // timeout - testFetchList(good, {{"/sleep/13", "took too long", ssl, true, true}}); + testFetchList( + good, + {{.path = "/sleep/13", + .msg = "took too long", + .ssl = ssl, + .failFetch = true, + .failApply = true}}); // bad manifest format using known versions // * Retrieves a v1 formatted list claiming version 2 - testFetchList(good, {{"/validators", "Missing fields", ssl, true, true, 2}}); + testFetchList( + good, + {{.path = "/validators", + .msg = "Missing fields", + .ssl = ssl, + .failFetch = true, + .failApply = true, + .serverVersion = 2}}); // * Retrieves a v2 formatted list claiming version 1 - testFetchList(good, {{"/validators2", "Missing fields", ssl, true, true, 0}}); + testFetchList( + good, + {{.path = "/validators2", + .msg = "Missing fields", + .ssl = ssl, + .failFetch = true, + .failApply = true, + .serverVersion = 0}}); // bad manifest version // Because versions other than 1 are treated as v2, the v1 // list won't have the blobs_v2 fields, and thus will claim to have // missing fields - testFetchList(good, {{"/validators", "Missing fields", ssl, true, true, 4}}); - testFetchList(good, {{"/validators2", "1 unsupported version", ssl, false, true, 4}}); + testFetchList( + good, + {{.path = "/validators", + .msg = "Missing fields", + .ssl = ssl, + .failFetch = true, + .failApply = true, + .serverVersion = 4}}); + testFetchList( + good, + {{.path = "/validators2", + .msg = "1 unsupported version", + .ssl = ssl, + .failFetch = false, + .failApply = true, + .serverVersion = 4}}); using namespace std::chrono_literals; // get expired validator list testFetchList( good, - {{"/validators", "Applied 1 expired validator list(s)", ssl, false, false, 1, 0s}}); + {{.path = "/validators", + .msg = "Applied 1 expired validator list(s)", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = 0s}}); testFetchList( good, - {{"/validators2", - "Applied 1 expired validator list(s)", - ssl, - false, - false, - 1, - 0s, - -1s}}); + {{.path = "/validators2", + .msg = "Applied 1 expired validator list(s)", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = 0s, + .effectiveOverlap = -1s}}); // force an out-of-range validUntil value testFetchList( good, - {{"/validators", - "1 invalid validator list(s)", - ssl, - false, - true, - 1, - std::chrono::seconds{json::Value::kMinInt}}}); + {{.path = "/validators", + .msg = "1 invalid validator list(s)", + .ssl = ssl, + .failFetch = false, + .failApply = true, + .serverVersion = 1, + .expiresFromNow = std::chrono::seconds{json::Value::kMinInt}}}); // force an out-of-range validUntil value on the future list // The first list is accepted. The second fails. The parser // returns the "best" result, so this looks like a success. testFetchList( good, - {{"/validators2", - "", - ssl, - false, - false, - 1, - std::chrono::seconds{json::Value::kMaxInt - 300}, - 299s}}); + {{.path = "/validators2", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = std::chrono::seconds{json::Value::kMaxInt - 300}, + .effectiveOverlap = 299s}}); // force an out-of-range validFrom value // The first list is accepted. The second fails. The parser // returns the "best" result, so this looks like a success. testFetchList( good, - {{"/validators2", - "", - ssl, - false, - false, - 1, - std::chrono::seconds{json::Value::kMaxInt - 300}, - 301s}}); + {{.path = "/validators2", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = std::chrono::seconds{json::Value::kMaxInt - 300}, + .effectiveOverlap = 301s}}); // force an out-of-range validUntil value on _both_ lists testFetchList( good, - {{"/validators2", - "2 invalid validator list(s)", - ssl, - false, - true, - 1, - std::chrono::seconds{json::Value::kMinInt}, - std::chrono::seconds{json::Value::kMaxInt - 6000}}}); + {{.path = "/validators2", + .msg = "2 invalid validator list(s)", + .ssl = ssl, + .failFetch = false, + .failApply = true, + .serverVersion = 1, + .expiresFromNow = std::chrono::seconds{json::Value::kMinInt}, + .effectiveOverlap = std::chrono::seconds{json::Value::kMaxInt - 6000}}}); // verify refresh intervals are properly clamped testFetchList( good, - {{"/validators/refresh/0", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 1}}); // minimum of 1 minute + {{.path = "/validators/refresh/0", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 1}}); // minimum of 1 minute testFetchList( good, - {{"/validators2/refresh/0", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 1}}); // minimum of 1 minute + {{.path = "/validators2/refresh/0", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 1}}); // minimum of 1 minute testFetchList( good, - {{"/validators/refresh/10", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 10}}); // 10 minutes is fine + {{.path = "/validators/refresh/10", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 10}}); // 10 minutes is fine testFetchList( good, - {{"/validators2/refresh/10", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 10}}); // 10 minutes is fine + {{.path = "/validators2/refresh/10", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 10}}); // 10 minutes is fine testFetchList( good, - {{"/validators/refresh/2000", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 60 * 24}}); // max of 24 hours + {{.path = "/validators/refresh/2000", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 60 * 24}}); // max of 24 hours testFetchList( good, - {{"/validators2/refresh/2000", - "", - ssl, - false, - false, - 1, - detail::kDefaultExpires, - detail::kDefaultEffectiveOverlap, - 60 * 24}}); // max of 24 hours + {{.path = "/validators2/refresh/2000", + .msg = "", + .ssl = ssl, + .failFetch = false, + .failApply = false, + .serverVersion = 1, + .expiresFromNow = detail::kDefaultExpires, + .effectiveOverlap = detail::kDefaultEffectiveOverlap, + .expectedRefreshMin = 60 * 24}}); // max of 24 hours } using namespace boost::filesystem; for (auto const& file : directory_iterator(good.subdir())) diff --git a/src/test/basics/PerfLog_test.cpp b/src/test/basics/PerfLog_test.cpp index c370c241c5..41b5f81f5d 100644 --- a/src/test/basics/PerfLog_test.cpp +++ b/src/test/basics/PerfLog_test.cpp @@ -619,7 +619,7 @@ public: // Total queued duration is triangle number of (i + 1). BEAST_EXPECT( - jsonToUInt64(total[jss::queued_duration_us]) == (((i * i) + 3 * i + 2) / 2)); + jsonToUInt64(total[jss::queued_duration_us]) == (((i * i) + (3 * i) + 2) / 2)); BEAST_EXPECT(total[jss::running_duration_us] == "0"); } diff --git a/src/test/beast/aged_associative_container_test.cpp b/src/test/beast/aged_associative_container_test.cpp index f2ce72b584..d7f74aaa7d 100644 --- a/src/test/beast/aged_associative_container_test.cpp +++ b/src/test/beast/aged_associative_container_test.cpp @@ -414,11 +414,11 @@ public: // unordered template - std::enable_if_t::type::is_unordered::value> + std::enable_if_t::is_unordered::value> checkUnorderedContentsRefRef(C&& c, Values const& v); template - std::enable_if_t::type::is_unordered::value> + std::enable_if_t::is_unordered::value> checkUnorderedContentsRefRef(C&&, Values const&) { } @@ -641,7 +641,7 @@ AgedAssociativeContainerTestBase::checkMapContents(Container& c, Values const& v // unordered template -std::enable_if_t::type::is_unordered::value> +std::enable_if_t::is_unordered::value> AgedAssociativeContainerTestBase::checkUnorderedContentsRefRef(C&& c, Values const& v) { using Cont = std::remove_reference_t; diff --git a/src/test/core/Config_test.cpp b/src/test/core/Config_test.cpp index ce6774827e..92f59fe644 100644 --- a/src/test/core/Config_test.cpp +++ b/src/test/core/Config_test.cpp @@ -1457,14 +1457,14 @@ r.ripple.com:51235 }; std::vector const units = { - {"seconds", 1, 15 * 60, false}, - {"minutes", 60, 14, false}, - {"minutes", 60, 15, true}, - {"hours", 3600, 10, true}, - {"days", 86400, 10, true}, - {"weeks", 604800, 2, true}, - {"months", 2592000, 1, false}, - {"years", 31536000, 1, false}}; + {.unit = "seconds", .numSeconds = 1, .configVal = 15 * 60, .shouldPass = false}, + {.unit = "minutes", .numSeconds = 60, .configVal = 14, .shouldPass = false}, + {.unit = "minutes", .numSeconds = 60, .configVal = 15, .shouldPass = true}, + {.unit = "hours", .numSeconds = 3600, .configVal = 10, .shouldPass = true}, + {.unit = "days", .numSeconds = 86400, .configVal = 10, .shouldPass = true}, + {.unit = "weeks", .numSeconds = 604800, .configVal = 2, .shouldPass = true}, + {.unit = "months", .numSeconds = 2592000, .configVal = 1, .shouldPass = false}, + {.unit = "years", .numSeconds = 31536000, .configVal = 1, .shouldPass = false}}; std::string space; for (auto& [unit, sec, val, shouldPass] : units) diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index 551fd1404b..6d069523d2 100644 --- a/src/test/jtx/impl/WSClient.cpp +++ b/src/test/jtx/impl/WSClient.cpp @@ -70,7 +70,7 @@ class WSClientImpl : public WSClient continue; ParsedPort pp; parsePort(pp, cfg[name], log); - if (pp.protocol.count(ps) == 0) + if (!pp.protocol.contains(ps)) continue; using namespace boost::asio::ip; if (pp.ip && pp.ip->is_unspecified()) diff --git a/src/test/jtx/impl/permissioned_dex.cpp b/src/test/jtx/impl/permissioned_dex.cpp index 5c059e9e80..a6b24d7ac6 100644 --- a/src/test/jtx/impl/permissioned_dex.cpp +++ b/src/test/jtx/impl/permissioned_dex.cpp @@ -26,7 +26,7 @@ setupDomain( env.fund(XRP(100000), domainOwner); env.close(); - pdomain::Credentials const credentials{{domainOwner, credType}}; + pdomain::Credentials const credentials{{.issuer = domainOwner, .credType = credType}}; env(pdomain::setTx(domainOwner, credentials)); auto const objects = pdomain::getObjects(domainOwner, env); diff --git a/src/test/jtx/impl/permissioned_domains.cpp b/src/test/jtx/impl/permissioned_domains.cpp index 690451c7d8..385008be43 100644 --- a/src/test/jtx/impl/permissioned_domains.cpp +++ b/src/test/jtx/impl/permissioned_domains.cpp @@ -130,7 +130,9 @@ credentialsFromJson( auto const& credentialType = obj["CredentialType"]; // NOLINTNEXTLINE(bugprone-unchecked-optional-access): used only in tests auto blob = strUnHex(credentialType.asString()).value(); - ret.push_back({human2Acc.at(issuer.asString()), std::string(blob.begin(), blob.end())}); + ret.push_back( + {.issuer = human2Acc.at(issuer.asString()), + .credType = std::string(blob.begin(), blob.end())}); } return ret; } diff --git a/src/test/nodestore/import_test.cpp b/src/test/nodestore/import_test.cpp index a80b5ccc93..de99edd655 100644 --- a/src/test/nodestore/import_test.cpp +++ b/src/test/nodestore/import_test.cpp @@ -297,17 +297,17 @@ public: auto const args = parseArgs(arg()); bool usage = args.empty(); - if (!usage && args.find("from") == args.end()) + if (!usage && !args.contains("from")) { log << "Missing parameter: from"; usage = true; } - if (!usage && args.find("to") == args.end()) + if (!usage && !args.contains("to")) { log << "Missing parameter: to"; usage = true; } - if (!usage && args.find("buffer") == args.end()) + if (!usage && !args.contains("buffer")) { log << "Missing parameter: buffer"; usage = true; diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 4307b7ab7f..31b20b37d4 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -692,11 +692,11 @@ public: { std::string const credentialType1 = "credential1"; - Account issuer("issuer"); + Account const issuer("issuer"); env.fund(XRP(5000), issuer); // gw creates an PermissionedDomain. - env(pdomain::setTx(gw, {{issuer, credentialType1}})); + env(pdomain::setTx(gw, {{.issuer = issuer, .credType = credentialType1}})); env.close(); // Find the PermissionedDomain. diff --git a/src/test/rpc/DepositAuthorized_test.cpp b/src/test/rpc/DepositAuthorized_test.cpp index 89053557a7..e6720602c9 100644 --- a/src/test/rpc/DepositAuthorized_test.cpp +++ b/src/test/rpc/DepositAuthorized_test.cpp @@ -324,7 +324,7 @@ public: env.close(); // becky authorize any account recognized by carol to make a payment - env(deposit::authCredentials(becky, {{carol, credType}})); + env(deposit::authCredentials(becky, {{.issuer = carol, .credType = credType}})); env.close(); { @@ -507,7 +507,7 @@ public: env.close(); // becky authorize any account recognized by carol to make a payment - env(deposit::authCredentials(becky, {{carol, credType2}})); + env(deposit::authCredentials(becky, {{.issuer = carol, .credType = credType2}})); env.close(); { diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index d231a2d4a0..dd9eb1c119 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -784,8 +784,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::amm, { - {jss::asset, "malformedRequest"}, - {jss::asset2, "malformedRequest"}, + {.fieldName = jss::asset, .malformedErrorMsg = "malformedRequest"}, + {.fieldName = jss::asset2, .malformedErrorMsg = "malformedRequest"}, }); }; auto getIOU = [&](Env& env) -> PrettyAsset { return alice["USD"]; }; @@ -900,9 +900,9 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::credential, { - {jss::subject, "malformedRequest"}, - {jss::issuer, "malformedRequest"}, - {jss::credential_type, "malformedRequest"}, + {.fieldName = jss::subject, .malformedErrorMsg = "malformedRequest"}, + {.fieldName = jss::issuer, .malformedErrorMsg = "malformedRequest"}, + {.fieldName = jss::credential_type, .malformedErrorMsg = "malformedRequest"}, }); } } @@ -954,8 +954,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::delegate, { - {jss::account, "malformedAddress"}, - {jss::authorize, "malformedAddress"}, + {.fieldName = jss::account, .malformedErrorMsg = "malformedAddress"}, + {.fieldName = jss::authorize, .malformedErrorMsg = "malformedAddress"}, }); } } @@ -1011,8 +1011,10 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::deposit_preauth, { - {jss::owner, "malformedOwner"}, - {jss::authorized, "malformedAuthorized", false}, + {.fieldName = jss::owner, .malformedErrorMsg = "malformedOwner"}, + {.fieldName = jss::authorized, + .malformedErrorMsg = "malformedAuthorized", + .required = false}, }); } } @@ -1037,7 +1039,7 @@ class LedgerEntry_test : public beast::unit_test::Suite // Setup Bob with DepositAuth env(fset(bob, asfDepositAuth)); env.close(); - env(deposit::authCredentials(bob, {{issuer, credType}})); + env(deposit::authCredentials(bob, {{.issuer = issuer, .credType = credType}})); env.close(); } @@ -1458,7 +1460,10 @@ class LedgerEntry_test : public beast::unit_test::Suite { // Malformed escrow fields runLedgerEntryTest( - env, jss::escrow, {{jss::owner, "malformedOwner"}, {jss::seq, "malformedSeq"}}); + env, + jss::escrow, + {{.fieldName = jss::owner, .malformedErrorMsg = "malformedOwner"}, + {.fieldName = jss::seq, .malformedErrorMsg = "malformedSeq"}}); } } @@ -1667,7 +1672,8 @@ class LedgerEntry_test : public beast::unit_test::Suite runLedgerEntryTest( env, jss::offer, - {{jss::account, "malformedAddress"}, {jss::seq, "malformedRequest"}}); + {{.fieldName = jss::account, .malformedErrorMsg = "malformedAddress"}, + {.fieldName = jss::seq, .malformedErrorMsg = "malformedRequest"}}); } } @@ -1774,8 +1780,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, fieldName, { - {jss::accounts, "malformedRequest"}, - {jss::currency, "malformedCurrency"}, + {.fieldName = jss::accounts, .malformedErrorMsg = "malformedRequest"}, + {.fieldName = jss::currency, .malformedErrorMsg = "malformedCurrency"}, }); } { @@ -1955,8 +1961,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::ticket, { - {jss::account, "malformedAddress"}, - {jss::ticket_seq, "malformedRequest"}, + {.fieldName = jss::account, .malformedErrorMsg = "malformedAddress"}, + {.fieldName = jss::ticket_seq, .malformedErrorMsg = "malformedRequest"}, }); } } @@ -2034,8 +2040,9 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::oracle, { - {jss::account, "malformedAccount"}, - {jss::oracle_document_id, "malformedDocumentID"}, + {.fieldName = jss::account, .malformedErrorMsg = "malformedAccount"}, + {.fieldName = jss::oracle_document_id, + .malformedErrorMsg = "malformedDocumentID"}, }); } } @@ -2172,7 +2179,7 @@ class LedgerEntry_test : public beast::unit_test::Suite env.close(); auto const seq = env.seq(alice); - env(pdomain::setTx(alice, {{alice, "first credential"}})); + env(pdomain::setTx(alice, {{.issuer = alice, .credType = "first credential"}})); env.close(); auto const objects = pdomain::getObjects(alice, env); if (!BEAST_EXPECT(objects.size() == 1)) @@ -2221,8 +2228,8 @@ class LedgerEntry_test : public beast::unit_test::Suite env, jss::permissioned_domain, { - {jss::account, "malformedAddress"}, - {jss::seq, "malformedRequest"}, + {.fieldName = jss::account, .malformedErrorMsg = "malformedAddress"}, + {.fieldName = jss::seq, .malformedErrorMsg = "malformedRequest"}, }); } } diff --git a/src/xrpld/app/misc/detail/ValidatorList.cpp b/src/xrpld/app/misc/detail/ValidatorList.cpp index 57b65814e1..0981c32050 100644 --- a/src/xrpld/app/misc/detail/ValidatorList.cpp +++ b/src/xrpld/app/misc/detail/ValidatorList.cpp @@ -455,7 +455,7 @@ ValidatorList::parseBlobs(std::uint32_t version, json::Value const& body) std::vector ValidatorList::parseBlobs(protocol::TMValidatorList const& body) { - return {{body.blob(), body.signature(), {}}}; + return {{.blob = body.blob(), .signature = body.signature(), .manifest = {}}}; } // static diff --git a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp index fcf30fa4f4..5d276dc9c5 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp @@ -18,7 +18,8 @@ Config::Config() : outPeers(calcOutPeers()) std::size_t Config::calcOutPeers() const { - return std::max((maxPeers * Tuning::kOutPercent + 50) / 100, std::size_t(Tuning::kMinOutCount)); + return std::max( + ((maxPeers * Tuning::kOutPercent) + 50) / 100, std::size_t(Tuning::kMinOutCount)); } void diff --git a/src/xrpld/rpc/detail/Pathfinder.cpp b/src/xrpld/rpc/detail/Pathfinder.cpp index daa50cfb07..25da86ef8f 100644 --- a/src/xrpld/rpc/detail/Pathfinder.cpp +++ b/src/xrpld/rpc/detail/Pathfinder.cpp @@ -568,7 +568,11 @@ Pathfinder::rankPaths( JLOG(j_.debug()) << "findPaths: quality: " << uQuality << ": " << currentPath.getJson(JsonOptions::Values::None); - rankedPaths.push_back({uQuality, currentPath.size(), liquidity, i}); + rankedPaths.push_back( + {.quality = uQuality, + .length = currentPath.size(), + .liquidity = liquidity, + .index = i}); } } } @@ -1373,7 +1377,7 @@ fillPaths(Pathfinder::PaymentType type, PathCostList const& costs) auto& list = gPathTable[type]; XRPL_ASSERT(list.empty(), "xrpl::fillPaths : empty paths"); for (auto& cost : costs) - list.push_back({cost.cost, makePath(cost.path)}); + list.push_back({.searchLevel = cost.cost, .type = makePath(cost.path)}); } } // namespace @@ -1396,58 +1400,58 @@ Pathfinder::initPathTable() fillPaths( PaymentType::XrpToNonXrp, - {{1, "sfd"}, // source -> book -> gateway - {3, "sfad"}, // source -> book -> account -> destination - {5, "sfaad"}, // source -> book -> account -> account -> destination - {6, "sbfd"}, // source -> book -> book -> destination - {8, "sbafd"}, // source -> book -> account -> book -> destination - {9, "sbfad"}, // source -> book -> book -> account -> destination - {10, "sbafad"}}); + {{.cost = 1, .path = "sfd"}, // source -> book -> gateway + {.cost = 3, .path = "sfad"}, // source -> book -> account -> destination + {.cost = 5, .path = "sfaad"}, // source -> book -> account -> account -> destination + {.cost = 6, .path = "sbfd"}, // source -> book -> book -> destination + {.cost = 8, .path = "sbafd"}, // source -> book -> account -> book -> destination + {.cost = 9, .path = "sbfad"}, // source -> book -> book -> account -> destination + {.cost = 10, .path = "sbafad"}}); fillPaths( PaymentType::NonXrpToXrp, - {{1, "sxd"}, // gateway buys XRP - {2, "saxd"}, // source -> gateway -> book(XRP) -> dest - {6, "saaxd"}, - {7, "sbxd"}, - {8, "sabxd"}, - {9, "sabaxd"}}); + {{.cost = 1, .path = "sxd"}, // gateway buys XRP + {.cost = 2, .path = "saxd"}, // source -> gateway -> book(XRP) -> dest + {.cost = 6, .path = "saaxd"}, + {.cost = 7, .path = "sbxd"}, + {.cost = 8, .path = "sabxd"}, + {.cost = 9, .path = "sabaxd"}}); // non-XRP to non-XRP (same currency) fillPaths( PaymentType::NonXrpToSame, { - {1, "sad"}, // source -> gateway -> destination - {1, "sfd"}, // source -> book -> destination - {4, "safd"}, // source -> gateway -> book -> destination - {4, "sfad"}, - {5, "saad"}, - {5, "sbfd"}, - {6, "sxfad"}, - {6, "safad"}, - {6, "saxfd"}, // source -> gateway -> book to XRP -> book -> - // destination - {6, "saxfad"}, - {6, "sabfd"}, // source -> gateway -> book -> book -> destination - {7, "saaad"}, + {.cost = 1, .path = "sad"}, // source -> gateway -> destination + {.cost = 1, .path = "sfd"}, // source -> book -> destination + {.cost = 4, .path = "safd"}, // source -> gateway -> book -> destination + {.cost = 4, .path = "sfad"}, + {.cost = 5, .path = "saad"}, + {.cost = 5, .path = "sbfd"}, + {.cost = 6, .path = "sxfad"}, + {.cost = 6, .path = "safad"}, + {.cost = 6, .path = "saxfd"}, // source -> gateway -> book to XRP -> book -> + // destination + {.cost = 6, .path = "saxfad"}, + {.cost = 6, .path = "sabfd"}, // source -> gateway -> book -> book -> destination + {.cost = 7, .path = "saaad"}, }); // non-XRP to non-XRP (different currency) fillPaths( PaymentType::NonXrpToNonXrp, { - {1, "sfad"}, - {1, "safd"}, - {3, "safad"}, - {4, "sxfd"}, - {5, "saxfd"}, - {5, "sxfad"}, - {5, "sbfd"}, - {6, "saxfad"}, - {6, "sabfd"}, - {7, "saafd"}, - {8, "saafad"}, - {9, "safaad"}, + {.cost = 1, .path = "sfad"}, + {.cost = 1, .path = "safd"}, + {.cost = 3, .path = "safad"}, + {.cost = 4, .path = "sxfd"}, + {.cost = 5, .path = "saxfd"}, + {.cost = 5, .path = "sxfad"}, + {.cost = 5, .path = "sbfd"}, + {.cost = 6, .path = "saxfad"}, + {.cost = 6, .path = "sabfd"}, + {.cost = 7, .path = "saafd"}, + {.cost = 8, .path = "saafad"}, + {.cost = 9, .path = "safaad"}, }); /* cspell: enable */ } diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index 0bdece3ec3..c73e474e18 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -165,10 +165,10 @@ ServerHandler::setup(Setup const& setup, beast::Journal journal) port.port = endpointPort; if ((setup_.client.port == 0u) && - (port.protocol.count("http") > 0 || port.protocol.count("https") > 0)) + (port.protocol.contains("http") || port.protocol.contains("https"))) setup_.client.port = endpointPort; - if ((setup_.overlay.port() == 0u) && (port.protocol.count("peer") > 0)) + if ((setup_.overlay.port() == 0u) && (port.protocol.contains("peer"))) setup_.overlay.port(endpointPort); } } @@ -217,7 +217,7 @@ ServerHandler::onHandoff( using namespace boost::beast; auto const& p{session.port().protocol}; bool const isWs{ - p.count("ws") > 0 || p.count("ws2") > 0 || p.count("wss") > 0 || p.count("wss2") > 0}; + p.contains("ws") || p.contains("ws2") || p.contains("wss") || p.contains("wss2")}; if (websocket::is_upgrade(request)) { @@ -251,7 +251,7 @@ ServerHandler::onHandoff( return handoff; } - if (bundle && p.count("peer") > 0) + if (bundle && p.contains("peer")) return app_.getOverlay().onHandoff(std::move(bundle), std::move(request), remoteAddress); if (isWs && isStatusRequest(request)) @@ -301,7 +301,7 @@ void ServerHandler::onRequest(Session& session) { // Make sure RPC is enabled on the port - if (session.port().protocol.count("http") == 0 && session.port().protocol.count("https") == 0) + if (!session.port().protocol.contains("http") && !session.port().protocol.contains("https")) { httpReply(403, "Forbidden", makeOutput(session), app_.getJournal("RPC")); session.close(true); @@ -1180,7 +1180,7 @@ parsePorts(Config const& config, std::ostream& log) else { auto const count = std::count_if(result.cbegin(), result.cend(), [](Port const& p) { - return p.protocol.count("peer") != 0; + return p.protocol.contains("peer"); }); if (count > 1) @@ -1203,12 +1203,12 @@ setupClient(ServerHandler::Setup& setup) decltype(setup.ports)::const_iterator iter; for (iter = setup.ports.cbegin(); iter != setup.ports.cend(); ++iter) { - if (iter->protocol.count("http") > 0 || iter->protocol.count("https") > 0) + if (iter->protocol.contains("http") || iter->protocol.contains("https")) break; } if (iter == setup.ports.cend()) return; - setup.client.secure = iter->protocol.count("https") > 0; + setup.client.secure = iter->protocol.contains("https"); if (beast::IP::isUnspecified(iter->ip)) { // VFALCO HACK! to make localhost work @@ -1230,7 +1230,7 @@ static void setupOverlay(ServerHandler::Setup& setup) { auto const iter = std::ranges::find_if( - setup.ports, [](Port const& port) { return port.protocol.count("peer") != 0; }); + setup.ports, [](Port const& port) { return port.protocol.contains("peer"); }); if (iter == setup.ports.cend()) { setup.overlay = {}; From 6571f75d399e3e237fbcb1ddfe23adbe3d98286c Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Fri, 5 Jun 2026 15:36:05 +0100 Subject: [PATCH 64/87] ci: Use multiple directories in dependabot config (#7413) --- .github/dependabot.yml | 40 ++++++---------------------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0e6b840fe7..da7a30dc77 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,40 +1,12 @@ version: 2 updates: - package-ecosystem: github-actions - directory: / - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/build-deps/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/generate-version/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/setup-conan/ + directories: + - / + - .github/actions/build-deps/ + - .github/actions/generate-version/ + - .github/actions/set-compiler-env/ + - .github/actions/setup-conan/ schedule: interval: weekly day: monday From 63ffdc39dc6bc1565b7e6437ed6686721c9379c2 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Fri, 5 Jun 2026 18:05:19 +0100 Subject: [PATCH 65/87] ci: Refactor build-related nix / docker / workflows (#7408) --- .github/scripts/strategy-matrix/linux.json | 2 +- .github/workflows/build-nix-image.yml | 109 ------------------ .github/workflows/build-nix-images.yml | 56 +++++++++ .github/workflows/build-packaging-images.yml | 48 ++++++++ .github/workflows/publish-docs.yml | 18 +-- .../workflows/reusable-build-docker-image.yml | 2 +- .../reusable-build-merge-docker-images.yml | 89 ++++++++++++++ .github/workflows/reusable-upload-recipe.yml | 2 +- .../nix.Dockerfile => nix/docker/Dockerfile | 12 +- {docker => nix/docker}/check-tools.sh | 0 .../docker}/install-sanitizer-libs.sh | 0 {docker => nix/docker}/loader-path.sh | 0 .../docker}/test_files/compile-cpp-sources.sh | 0 .../docker}/test_files/cpp_sources/asan.cpp | 0 .../test_files/cpp_sources/regular.cpp | 0 .../docker}/test_files/cpp_sources/tsan.cpp | 0 .../docker}/test_files/cpp_sources/ubsan.cpp | 0 .../docker}/test_files/run-test-binaries.sh | 0 package/Dockerfile | 14 +++ package/README.md | 13 +-- package/install-packaging-tools.sh | 68 +++++++++++ 21 files changed, 295 insertions(+), 138 deletions(-) delete mode 100644 .github/workflows/build-nix-image.yml create mode 100644 .github/workflows/build-nix-images.yml create mode 100644 .github/workflows/build-packaging-images.yml create mode 100644 .github/workflows/reusable-build-merge-docker-images.yml rename docker/nix.Dockerfile => nix/docker/Dockerfile (90%) rename {docker => nix/docker}/check-tools.sh (100%) rename {docker => nix/docker}/install-sanitizer-libs.sh (100%) rename {docker => nix/docker}/loader-path.sh (100%) rename {docker => nix/docker}/test_files/compile-cpp-sources.sh (100%) rename {docker => nix/docker}/test_files/cpp_sources/asan.cpp (100%) rename {docker => nix/docker}/test_files/cpp_sources/regular.cpp (100%) rename {docker => nix/docker}/test_files/cpp_sources/tsan.cpp (100%) rename {docker => nix/docker}/test_files/cpp_sources/ubsan.cpp (100%) rename {docker => nix/docker}/test_files/run-test-binaries.sh (100%) create mode 100644 package/Dockerfile create mode 100755 package/install-packaging-tools.sh diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index 3070b8d9f4..7da48a6a25 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -1,5 +1,5 @@ { - "image_tag": "sha-6c54342", + "image_tag": "sha-8abe82e", "configs": { "ubuntu": [ { diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml deleted file mode 100644 index bae4cfd437..0000000000 --- a/.github/workflows/build-nix-image.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: Build Nix Docker image - -on: - push: - branches: - - develop - paths: - - ".github/workflows/build-nix-image.yml" - - ".github/workflows/reusable-build-docker-image.yml" - - "docker/**" - - "flake.nix" - - "flake.lock" - - "nix/**" - pull_request: - paths: - - ".github/workflows/build-nix-image.yml" - - ".github/workflows/reusable-build-docker-image.yml" - - "docker/**" - - "flake.nix" - - "flake.lock" - - "nix/**" - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -defaults: - run: - shell: bash - -jobs: - build: - name: Build ${{ matrix.distro.name }} (${{ matrix.target.platform }}) - permissions: - contents: read - packages: write - strategy: - fail-fast: false - matrix: - # The base images are the oldest supported version of each distro - # that we want to build images for. - distro: - - name: nixos - base_image: nixos/nix:latest - - name: ubuntu - base_image: ubuntu:20.04 - - name: rhel - base_image: registry.access.redhat.com/ubi9/ubi:latest - - name: debian - base_image: debian:bookworm - target: - - platform: linux/amd64 - runner: ubuntu-latest - - platform: linux/arm64 - runner: ubuntu-24.04-arm - uses: ./.github/workflows/reusable-build-docker-image.yml - with: - image_name: ghcr.io/xrplf/xrpld/nix-${{ matrix.distro.name }} - dockerfile: docker/nix.Dockerfile - base_image: ${{ matrix.distro.base_image }} - platform: ${{ matrix.target.platform }} - runner: ${{ matrix.target.runner }} - push: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} - - merge: - name: Merge ${{ matrix.distro }} manifest - needs: build - if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - strategy: - fail-fast: false - matrix: - distro: [nixos, ubuntu, rhel, debian] - env: - IMAGE_NAME: ghcr.io/xrplf/xrpld/nix-${{ matrix.distro }} - - steps: - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - - name: Docker metadata - id: meta - uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 - with: - images: ${{ env.IMAGE_NAME }} - tags: | - type=sha,prefix=sha-,format=short - type=raw,value=latest - - - name: Login to GitHub Container Registry - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create multi-arch manifests - run: | - for tag in $(jq -cr '.tags[]' <<<"$DOCKER_METADATA_OUTPUT_JSON"); do - docker buildx imagetools create -t "$tag" "${tag}-amd64" "${tag}-arm64" - done - - - name: Inspect image - run: | - docker buildx imagetools inspect "${IMAGE_NAME}:${{ steps.meta.outputs.version }}" diff --git a/.github/workflows/build-nix-images.yml b/.github/workflows/build-nix-images.yml new file mode 100644 index 0000000000..dc02f84e0f --- /dev/null +++ b/.github/workflows/build-nix-images.yml @@ -0,0 +1,56 @@ +name: Build Nix Docker images + +on: + push: + branches: + - develop + paths: + - ".github/workflows/build-nix-images.yml" + - ".github/workflows/reusable-build-docker-image.yml" + - ".github/workflows/reusable-build-merge-docker-images.yml" + - "flake.nix" + - "flake.lock" + - "nix/**" + pull_request: + paths: + - ".github/workflows/build-nix-images.yml" + - ".github/workflows/reusable-build-docker-image.yml" + - ".github/workflows/reusable-build-merge-docker-images.yml" + - "flake.nix" + - "flake.lock" + - "nix/**" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + build-merge: + name: Build and push nix-${{ matrix.distro.name }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + # The base images are the oldest supported version of each distro + # that we want to build images for. + distro: + - name: nixos + base_image: nixos/nix:latest + - name: ubuntu + base_image: ubuntu:20.04 + - name: debian + base_image: debian:bookworm + - name: rhel + base_image: registry.access.redhat.com/ubi9/ubi:latest + uses: ./.github/workflows/reusable-build-merge-docker-images.yml + with: + image_name: ghcr.io/xrplf/xrpld/nix-${{ matrix.distro.name }} + dockerfile: nix/docker/Dockerfile + base_image: ${{ matrix.distro.base_image }} diff --git a/.github/workflows/build-packaging-images.yml b/.github/workflows/build-packaging-images.yml new file mode 100644 index 0000000000..a11a16f298 --- /dev/null +++ b/.github/workflows/build-packaging-images.yml @@ -0,0 +1,48 @@ +name: Build packaging Docker images + +on: + push: + branches: + - develop + paths: + - ".github/workflows/build-packaging-images.yml" + - ".github/workflows/reusable-build-docker-image.yml" + - ".github/workflows/reusable-build-merge-docker-images.yml" + - "package/Dockerfile" + - "package/install-packaging-tools.sh" + pull_request: + paths: + - ".github/workflows/build-packaging-images.yml" + - ".github/workflows/reusable-build-docker-image.yml" + - ".github/workflows/reusable-build-merge-docker-images.yml" + - "package/Dockerfile" + - "package/install-packaging-tools.sh" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + build-merge: + name: Build and push packaging-${{ matrix.distro.name }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + distro: + - name: debian + base_image: debian:bookworm + - name: rhel + base_image: registry.access.redhat.com/ubi9/ubi:latest + uses: ./.github/workflows/reusable-build-merge-docker-images.yml + with: + image_name: ghcr.io/xrplf/xrpld/packaging-${{ matrix.distro.name }} + dockerfile: package/Dockerfile + base_image: ${{ matrix.distro.base_image }} diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index d619be5543..bbe6ec4592 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -41,7 +41,7 @@ env: jobs: build: runs-on: ubuntu-latest - container: ghcr.io/xrplf/ci/tools-rippled-documentation:sha-a8c7be1 + container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-8abe82e steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -57,19 +57,11 @@ jobs: with: subtract: ${{ env.NPROC_SUBTRACT }} - - name: Check configuration - run: | - echo 'Checking path.' - echo ${PATH} | tr ':' '\n' + - name: Print build environment + uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 - echo 'Checking environment variables.' - env | sort - - echo 'Checking CMake version.' - cmake --version - - echo 'Checking Doxygen version.' - doxygen --version + - name: Check Doxygen version + run: doxygen --version - name: Build documentation env: diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index c3795e56fa..5830ef9ece 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -38,7 +38,7 @@ defaults: jobs: build: - name: Build (${{ inputs.platform }}) + name: Build ${{ inputs.platform }} runs-on: ${{ inputs.runner }} permissions: contents: read diff --git a/.github/workflows/reusable-build-merge-docker-images.yml b/.github/workflows/reusable-build-merge-docker-images.yml new file mode 100644 index 0000000000..98deb6ea3f --- /dev/null +++ b/.github/workflows/reusable-build-merge-docker-images.yml @@ -0,0 +1,89 @@ +name: Reusable build and merge Docker image (multi-arch) + +on: + workflow_call: + inputs: + image_name: + description: "Full image name without tag (e.g. 'ghcr.io/xrplf/xrpld/nix-ubuntu')" + required: true + type: string + dockerfile: + description: "Path to the Dockerfile, relative to the repository root" + required: true + type: string + base_image: + description: "Value passed to the Dockerfile as the BASE_IMAGE build arg" + required: true + type: string + +defaults: + run: + shell: bash + +jobs: + build: + name: Build ${{ inputs.image_name }} + permissions: + contents: read + packages: write + + strategy: + fail-fast: false + matrix: + target: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + + uses: ./.github/workflows/reusable-build-docker-image.yml + with: + image_name: ${{ inputs.image_name }} + dockerfile: ${{ inputs.dockerfile }} + base_image: ${{ inputs.base_image }} + platform: ${{ matrix.target.platform }} + runner: ${{ matrix.target.runner }} + push: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} + + merge: + name: Merge ${{ inputs.image_name }} + needs: build + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + + - name: Docker metadata + id: meta + uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 + with: + images: ${{ inputs.image_name }} + tags: | + type=sha,prefix=sha-,format=short + type=raw,value=latest + + - name: Login to GitHub Container Registry + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create multi-arch manifests + if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} + run: | + for tag in $(jq -cr '.tags[]' <<<"$DOCKER_METADATA_OUTPUT_JSON"); do + docker buildx imagetools create -t "$tag" "${tag}-amd64" "${tag}-arm64" + done + + - name: Inspect image + if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} + env: + IMAGE_NAME: ${{ inputs.image_name }} + IMAGE_VERSION: ${{ steps.meta.outputs.version }} + run: | + docker buildx imagetools inspect "${IMAGE_NAME}:${IMAGE_VERSION}" diff --git a/.github/workflows/reusable-upload-recipe.yml b/.github/workflows/reusable-upload-recipe.yml index d3fe0f356b..b7ec5f9ef7 100644 --- a/.github/workflows/reusable-upload-recipe.yml +++ b/.github/workflows/reusable-upload-recipe.yml @@ -40,7 +40,7 @@ defaults: jobs: upload: runs-on: ubuntu-latest - container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13-sha-5dd7158 + container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-8abe82e steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/docker/nix.Dockerfile b/nix/docker/Dockerfile similarity index 90% rename from docker/nix.Dockerfile rename to nix/docker/Dockerfile index 6248708417..e6df48e18c 100644 --- a/docker/nix.Dockerfile +++ b/nix/docker/Dockerfile @@ -56,7 +56,7 @@ ENV GIT_SSL_CAINFO="/nix/ci-env/etc/ssl/certs/ca-bundle.crt" # Externally-built dynamically-linked ELF binaries hard-code the loader path # (e.g. /lib64/ld-linux-x86-64.so.2) in their PT_INTERP header. Install it # from the Nix store when the base image doesn't already provide one. -COPY docker/loader-path.sh /tmp/loader-path.sh +COPY nix/docker/loader-path.sh /tmp/loader-path.sh RUN < deb, `dnf`/`yum` -> rpm). The image tag is composed as -`ghcr.io/xrplf/ci/{distro}-{version}:{compiler}-{cver}-sha-{image_sha}` — +`ghcr.io/xrplf/xrpld/packaging-:sha-` — the same scheme used by `reusable-build-test.yml`. Bump `image_sha` in `linux.json` and both CI and local builds pick up the new image with no workflow edits. | Package type | Image (derived from `linux.json`) | Tool required | | ------------ | ---------------------------------------------------- | --------------------------------------------------------------- | -| RPM | `ghcr.io/xrplf/ci/rhel-9:gcc-12-sha-` | `rpmbuild` | -| DEB | `ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12-sha-` | `dpkg-buildpackage`, `debhelper (>= 13)`, `dh-sequence-systemd` | +| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-` | `rpmbuild` | +| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-` | `dpkg-buildpackage`, `debhelper (>= 13)`, `dh-sequence-systemd` | To print the exact image tags for the current `linux.json`: ```bash -./.github/scripts/strategy-matrix/generate.py --packaging --config=.github/scripts/strategy-matrix/linux.json +./.github/scripts/strategy-matrix/generate.py --packaging ``` ## Building packages diff --git a/package/install-packaging-tools.sh b/package/install-packaging-tools.sh new file mode 100755 index 0000000000..a26159a204 --- /dev/null +++ b/package/install-packaging-tools.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +set -euo pipefail + +if [ ! -f /etc/os-release ]; then + echo "ERROR: /etc/os-release not found; cannot detect OS" >&2 + exit 1 +fi + +# shellcheck source=/dev/null +. /etc/os-release + +echo "Detected OS: ${ID} ${VERSION_ID:-}" + +case "${ID}" in + ubuntu | debian | rhel | centos | rocky | almalinux) + echo "Supported OS detected: ${ID}" + ;; + *) + echo "ERROR: unsupported OS '${ID}'. Supported: debian, ubuntu, rhel-family" >&2 + exit 1 + ;; +esac + +function install() { + case "${ID}" in + debian | ubuntu) + apt-get update -y + apt-get install -y --no-install-recommends \ + ca-certificates \ + debhelper \ + debhelper-compat \ + dpkg-dev \ + git + ;; + + rhel | centos | rocky | almalinux) + dnf install -y --setopt=install_weak_deps=False \ + git \ + rpm-build \ + redhat-rpm-config \ + systemd-rpm-macros + ;; + esac +} + +function postinstall() { + # Don't clear cache in non-CI environments + if [ -z "${CI:-}" ]; then + echo "Not running in CI environment; skipping cache cleanup" + return + fi + + case "${ID}" in + debian | ubuntu) + apt-get clean + rm -rf /var/lib/apt/lists/* + ;; + + rhel | centos | rocky | almalinux) + dnf clean -y all + rm -rf /var/cache/dnf/* + ;; + esac +} + +install +postinstall From fc57dab78bd7dc4a9a022c16fd557b18f7a5e571 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 17:17:47 +0000 Subject: [PATCH 66/87] ci: [DEPENDABOT] bump actions/checkout from 6.0.2 to 6.0.3 (#7414) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-pr-description.yml | 2 +- .github/workflows/on-pr.yml | 2 +- .github/workflows/publish-docs.yml | 2 +- .github/workflows/reusable-build-docker-image.yml | 2 +- .github/workflows/reusable-build-test-config.yml | 2 +- .github/workflows/reusable-check-levelization.yml | 2 +- .github/workflows/reusable-check-rename.yml | 2 +- .github/workflows/reusable-clang-tidy.yml | 2 +- .github/workflows/reusable-package.yml | 6 +++--- .github/workflows/reusable-strategy-matrix.yml | 2 +- .github/workflows/reusable-upload-recipe.yml | 2 +- .github/workflows/upload-conan-deps.yml | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/check-pr-description.yml b/.github/workflows/check-pr-description.yml index ff28220171..a60b83738a 100644 --- a/.github/workflows/check-pr-description.yml +++ b/.github/workflows/check-pr-description.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Write PR body to file env: diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index db3c8667e5..4b2edeb93d 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Determine changed files # This step checks whether any files have changed that should # cause the next jobs to run. We do it this way rather than diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index bbe6ec4592..35f33b6446 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -44,7 +44,7 @@ jobs: container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-8abe82e steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Prepare runner uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml index 5830ef9ece..253563c6a5 100644 --- a/.github/workflows/reusable-build-docker-image.yml +++ b/.github/workflows/reusable-build-docker-image.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Determine arch id: vars diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index e1154f74be..6f0750e3f7 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -110,7 +110,7 @@ jobs: uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Prepare runner uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab diff --git a/.github/workflows/reusable-check-levelization.yml b/.github/workflows/reusable-check-levelization.yml index b5d57a177a..813c0e1e36 100644 --- a/.github/workflows/reusable-check-levelization.yml +++ b/.github/workflows/reusable-check-levelization.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Check levelization run: python .github/scripts/levelization/generate.py - name: Check for differences diff --git a/.github/workflows/reusable-check-rename.yml b/.github/workflows/reusable-check-rename.yml index 7aa5b80594..5002cc7f40 100644 --- a/.github/workflows/reusable-check-rename.yml +++ b/.github/workflows/reusable-check-rename.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Check definitions run: .github/scripts/rename/definitions.sh . - name: Check copyright notices diff --git a/.github/workflows/reusable-clang-tidy.yml b/.github/workflows/reusable-clang-tidy.yml index cfbd8af963..31e06d05eb 100644 --- a/.github/workflows/reusable-clang-tidy.yml +++ b/.github/workflows/reusable-clang-tidy.yml @@ -42,7 +42,7 @@ jobs: issues: write steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Prepare runner uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index 670c01733e..890277d184 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -27,7 +27,7 @@ jobs: matrix: ${{ steps.generate.outputs.matrix }} steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -45,7 +45,7 @@ jobs: version: ${{ steps.version.outputs.version }} steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: sparse-checkout: | .github/actions/generate-version @@ -94,7 +94,7 @@ jobs: systemd-rpm-macros - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Download pre-built binary uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index 16a2b4e336..ea134b43b2 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -23,7 +23,7 @@ jobs: matrix: ${{ steps.generate.outputs.matrix }} steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/.github/workflows/reusable-upload-recipe.yml b/.github/workflows/reusable-upload-recipe.yml index b7ec5f9ef7..6e1ea943ca 100644 --- a/.github/workflows/reusable-upload-recipe.yml +++ b/.github/workflows/reusable-upload-recipe.yml @@ -43,7 +43,7 @@ jobs: container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-8abe82e steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Generate build version number id: version diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index 87465b4d3d..6310c90899 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -64,7 +64,7 @@ jobs: uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Prepare runner uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab From 949887feb9f32b49829e9c29712697f567b23916 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Fri, 5 Jun 2026 20:24:32 +0100 Subject: [PATCH 67/87] build: Create single test binary xrpl_tests (#7327) --- .gersemi/definitions.cmake | 3 - .github/scripts/rename/cmake.sh | 4 - .../workflows/reusable-build-test-config.yml | 21 ++--- cmake/XrplAddTest.cmake | 22 ----- cmake/XrplCov.cmake | 2 +- src/tests/libxrpl/CMakeLists.txt | 89 ++++++++++--------- src/tests/libxrpl/crypto/main.cpp | 8 -- src/tests/libxrpl/json/main.cpp | 8 -- src/tests/libxrpl/{basics => }/main.cpp | 0 src/tests/libxrpl/net/main.cpp | 8 -- src/tests/libxrpl/tx/main.cpp | 8 -- 11 files changed, 59 insertions(+), 114 deletions(-) delete mode 100644 cmake/XrplAddTest.cmake delete mode 100644 src/tests/libxrpl/crypto/main.cpp delete mode 100644 src/tests/libxrpl/json/main.cpp rename src/tests/libxrpl/{basics => }/main.cpp (100%) delete mode 100644 src/tests/libxrpl/net/main.cpp delete mode 100644 src/tests/libxrpl/tx/main.cpp diff --git a/.gersemi/definitions.cmake b/.gersemi/definitions.cmake index a16e330ffa..245f827f90 100644 --- a/.gersemi/definitions.cmake +++ b/.gersemi/definitions.cmake @@ -11,9 +11,6 @@ endfunction() function(create_symbolic_link target link) endfunction() -function(xrpl_add_test name) -endfunction() - macro(exclude_from_default target_) endmacro() diff --git a/.github/scripts/rename/cmake.sh b/.github/scripts/rename/cmake.sh index 28bf777fed..3539f563e0 100755 --- a/.github/scripts/rename/cmake.sh +++ b/.github/scripts/rename/cmake.sh @@ -43,9 +43,6 @@ pushd "${DIRECTORY}" # Rename the files. find cmake -type f -name 'Rippled*.cmake' -exec bash -c 'mv "${1}" "${1/Rippled/Xrpl}"' - {} \; find cmake -type f -name 'Ripple*.cmake' -exec bash -c 'mv "${1}" "${1/Ripple/Xrpl}"' - {} \; -if [ -e cmake/xrpl_add_test.cmake ]; then - mv cmake/xrpl_add_test.cmake cmake/XrplAddTest.cmake -fi if [ -e include/xrpl/proto/ripple.proto ]; then mv include/xrpl/proto/ripple.proto include/xrpl/proto/xrpl.proto fi @@ -60,7 +57,6 @@ find cmake -type f -name '*.cmake' | while read -r FILE; do done ${SED_COMMAND} -i -E 's/Rippled?/Xrpl/g' CMakeLists.txt ${SED_COMMAND} -i 's/ripple/xrpl/g' CMakeLists.txt -${SED_COMMAND} -i 's/include(xrpl_add_test)/include(XrplAddTest)/' src/tests/libxrpl/CMakeLists.txt ${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' include/xrpl/protocol/messages.h ${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' BUILD.md ${SED_COMMAND} -i 's/ripple.pb.h/xrpl.pb.h/' BUILD.md diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index 6f0750e3f7..c215540b2e 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -236,6 +236,15 @@ jobs: retention-days: 3 if-no-files-found: error + - name: Upload the test binary (Linux) + if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: xrpl_tests-${{ inputs.config_name }} + path: ${{ env.BUILD_DIR }}/xrpl_tests + retention-days: 3 + if-no-files-found: error + - name: Export server definitions if: ${{ runner.os != 'Windows' && !inputs.build_only && env.VOIDSTAR_ENABLED != 'true' }} working-directory: ${{ env.BUILD_DIR }} @@ -286,16 +295,8 @@ jobs: - name: Run the separate tests if: ${{ !inputs.build_only }} - working-directory: ${{ env.BUILD_DIR }} - # Windows locks some of the build files while running tests, and parallel jobs can collide - env: - BUILD_TYPE: ${{ inputs.build_type }} - PARALLELISM: ${{ runner.os == 'Windows' && '1' || steps.nproc.outputs.nproc }} - run: | - ctest \ - --output-on-failure \ - -C "${BUILD_TYPE}" \ - -j "${PARALLELISM}" + working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} + run: ./xrpl_tests - name: Run the embedded tests if: ${{ !inputs.build_only }} diff --git a/cmake/XrplAddTest.cmake b/cmake/XrplAddTest.cmake deleted file mode 100644 index 2f1209e03c..0000000000 --- a/cmake/XrplAddTest.cmake +++ /dev/null @@ -1,22 +0,0 @@ -include(isolate_headers) - -function(xrpl_add_test name) - set(target ${PROJECT_NAME}.test.${name}) - - file( - GLOB_RECURSE sources - CONFIGURE_DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp" - ) - add_executable(${target} ${ARGN} ${sources}) - - isolate_headers( - ${target} - "${CMAKE_SOURCE_DIR}" - "${CMAKE_SOURCE_DIR}/tests/${name}" - PRIVATE - ) - - add_test(NAME ${target} COMMAND ${target}) -endfunction() diff --git a/cmake/XrplCov.cmake b/cmake/XrplCov.cmake index d81d7e689f..86ba534a88 100644 --- a/cmake/XrplCov.cmake +++ b/cmake/XrplCov.cmake @@ -47,7 +47,7 @@ setup_target_for_coverage_gcovr( "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb" - DEPENDENCIES xrpld xrpl.tests + DEPENDENCIES xrpld xrpl_tests ) add_code_coverage_to_target(opts INTERFACE) diff --git a/src/tests/libxrpl/CMakeLists.txt b/src/tests/libxrpl/CMakeLists.txt index ee07698519..60288e5f20 100644 --- a/src/tests/libxrpl/CMakeLists.txt +++ b/src/tests/libxrpl/CMakeLists.txt @@ -1,51 +1,56 @@ -include(XrplAddTest) +include(GoogleTest) +include(isolate_headers) # Test requirements. find_package(GTest REQUIRED) -# Custom target for all tests defined in this file -add_custom_target(xrpl.tests) - -# Test helpers -add_library(xrpl.helpers.test STATIC) -target_sources( - xrpl.helpers.test - PRIVATE helpers/Account.cpp helpers/TestSink.cpp helpers/TxTest.cpp +# Single combined gtest binary built from the shared test helpers and all test +# modules below. +add_executable( + xrpl_tests + main.cpp + helpers/Account.cpp + helpers/TestSink.cpp + helpers/TxTest.cpp ) -target_include_directories(xrpl.helpers.test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(xrpl.helpers.test PUBLIC xrpl.libxrpl gtest::gtest) - -# Common library dependencies for the rest of the tests. -add_library(xrpl.imports.test INTERFACE) -target_link_libraries( - xrpl.imports.test - INTERFACE gtest::gtest xrpl.libxrpl xrpl.helpers.test +set_target_properties( + xrpl_tests + PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" ) +# Lets test sources include the shared helpers as . +target_include_directories(xrpl_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(xrpl_tests PRIVATE GTest::gtest xrpl.libxrpl) -# One test for each module. -xrpl_add_test(basics) -target_link_libraries(xrpl.test.basics PRIVATE xrpl.imports.test) -add_dependencies(xrpl.tests xrpl.test.basics) - -xrpl_add_test(crypto) -target_link_libraries(xrpl.test.crypto PRIVATE xrpl.imports.test) -add_dependencies(xrpl.tests xrpl.test.crypto) - -xrpl_add_test(json) -target_link_libraries(xrpl.test.json PRIVATE xrpl.imports.test) -add_dependencies(xrpl.tests xrpl.test.json) - -xrpl_add_test(tx) -target_link_libraries(xrpl.test.tx PRIVATE xrpl.imports.test) -add_dependencies(xrpl.tests xrpl.test.tx) - -xrpl_add_test(protocol_autogen) -target_link_libraries(xrpl.test.protocol_autogen PRIVATE xrpl.imports.test) -add_dependencies(xrpl.tests xrpl.test.protocol_autogen) - -# Network unit tests are currently not supported on Windows +# One source subdirectory per module. Network unit tests are currently not +# supported on Windows. +set(test_modules + basics + crypto + json + tx + protocol_autogen +) if(NOT WIN32) - xrpl_add_test(net) - target_link_libraries(xrpl.test.net PRIVATE xrpl.imports.test) - add_dependencies(xrpl.tests xrpl.test.net) + list(APPEND test_modules net) endif() + +foreach(module IN LISTS test_modules) + # Append the module's sources (${module}/*.cpp and ${module}.cpp, if any). + file( + GLOB_RECURSE sources + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/${module}/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/${module}.cpp" + ) + target_sources(xrpl_tests PRIVATE ${sources}) + + # Expose the module's private headers under their canonical include path. + isolate_headers( + xrpl_tests + "${CMAKE_SOURCE_DIR}" + "${CMAKE_SOURCE_DIR}/tests/${module}" + PRIVATE + ) +endforeach() + +gtest_discover_tests(xrpl_tests) diff --git a/src/tests/libxrpl/crypto/main.cpp b/src/tests/libxrpl/crypto/main.cpp deleted file mode 100644 index 5142bbe08a..0000000000 --- a/src/tests/libxrpl/crypto/main.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int -main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/src/tests/libxrpl/json/main.cpp b/src/tests/libxrpl/json/main.cpp deleted file mode 100644 index 5142bbe08a..0000000000 --- a/src/tests/libxrpl/json/main.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int -main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/src/tests/libxrpl/basics/main.cpp b/src/tests/libxrpl/main.cpp similarity index 100% rename from src/tests/libxrpl/basics/main.cpp rename to src/tests/libxrpl/main.cpp diff --git a/src/tests/libxrpl/net/main.cpp b/src/tests/libxrpl/net/main.cpp deleted file mode 100644 index 5142bbe08a..0000000000 --- a/src/tests/libxrpl/net/main.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int -main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/src/tests/libxrpl/tx/main.cpp b/src/tests/libxrpl/tx/main.cpp deleted file mode 100644 index 5142bbe08a..0000000000 --- a/src/tests/libxrpl/tx/main.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int -main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} From 79f4ddc4a684d84701a1d97304c18285a7640cf8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 05:37:50 -0400 Subject: [PATCH 68/87] ci: [DEPENDABOT] bump codecov/codecov-action from 6.0.1 to 7.0.0 (#7426) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/reusable-build-test-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index c215540b2e..dc3336dd2a 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -370,7 +370,7 @@ jobs: - name: Upload coverage report if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: disable_search: true disable_telem: true From a389f922ddfacb5e28be461252ad1a57f5c4a265 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 8 Jun 2026 14:41:08 +0100 Subject: [PATCH 69/87] ci: Use new packaging images and don't cancel develop builds (#7417) Co-authored-by: Bart --- .github/scripts/strategy-matrix/linux.json | 6 ++--- .github/workflows/build-nix-images.yml | 3 ++- .github/workflows/build-packaging-images.yml | 3 ++- .github/workflows/pre-commit.yml | 2 +- .github/workflows/publish-docs.yml | 4 +-- .../workflows/reusable-build-test-config.yml | 2 +- .github/workflows/reusable-clang-tidy.yml | 6 ++--- .github/workflows/reusable-package.yml | 25 ------------------- .github/workflows/reusable-upload-recipe.yml | 2 +- .github/workflows/upload-conan-deps.yml | 2 +- src/tests/libxrpl/CMakeLists.txt | 2 +- 11 files changed, 17 insertions(+), 40 deletions(-) diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index 7da48a6a25..edacdbde4c 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -1,5 +1,5 @@ { - "image_tag": "sha-8abe82e", + "image_tag": "sha-63ffdc3", "configs": { "ubuntu": [ { @@ -67,7 +67,7 @@ "compiler": ["gcc"], "build_type": ["Release"], "arch": ["amd64"], - "image": "debian:bookworm" + "image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-63ffdc3" } ], @@ -76,7 +76,7 @@ "compiler": ["gcc"], "build_type": ["Release"], "arch": ["amd64"], - "image": "registry.access.redhat.com/ubi9/ubi:latest" + "image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-63ffdc3" } ] } diff --git a/.github/workflows/build-nix-images.yml b/.github/workflows/build-nix-images.yml index dc02f84e0f..4e38ca7c57 100644 --- a/.github/workflows/build-nix-images.yml +++ b/.github/workflows/build-nix-images.yml @@ -22,7 +22,8 @@ on: workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + # Read `on-trigger.yml` for the rationale behind this concurrency group name. + group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' && github.sha || github.ref }} cancel-in-progress: true defaults: diff --git a/.github/workflows/build-packaging-images.yml b/.github/workflows/build-packaging-images.yml index a11a16f298..c445dbf726 100644 --- a/.github/workflows/build-packaging-images.yml +++ b/.github/workflows/build-packaging-images.yml @@ -20,7 +20,8 @@ on: workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + # Read `on-trigger.yml` for the rationale behind this concurrency group name. + group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' && github.sha || github.ref }} cancel-in-progress: true defaults: diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index de6a4f40b4..aecf0c2a8b 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -14,7 +14,7 @@ on: jobs: # Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks. run-hooks: - uses: XRPLF/actions/.github/workflows/pre-commit.yml@cba1f0891650baf1a9c88624dc2d72573be2eb81 + uses: XRPLF/actions/.github/workflows/pre-commit.yml@312aaab296060ff89d7f798dcab59f019bea6e02 with: runs_on: ubuntu-latest container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-41ec7c1" }' diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 35f33b6446..bcf5968384 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -41,13 +41,13 @@ env: jobs: build: runs-on: ubuntu-latest - container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-8abe82e + container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-63ffdc3 steps: - name: Checkout repository uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab + uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8 with: enable_ccache: false diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index dc3336dd2a..d53cf97a39 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -113,7 +113,7 @@ jobs: uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab + uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8 with: enable_ccache: ${{ inputs.ccache_enabled }} diff --git a/.github/workflows/reusable-clang-tidy.yml b/.github/workflows/reusable-clang-tidy.yml index 31e06d05eb..9f10711b6f 100644 --- a/.github/workflows/reusable-clang-tidy.yml +++ b/.github/workflows/reusable-clang-tidy.yml @@ -29,14 +29,14 @@ jobs: if: ${{ inputs.check_only_changed }} permissions: contents: read - uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@224f3c48d3014d082a1129237b8291ff0b0a331f + uses: XRPLF/actions/.github/workflows/determine-tidy-files.yml@312aaab296060ff89d7f798dcab59f019bea6e02 run-clang-tidy: name: Run clang tidy needs: [determine-files] if: ${{ always() && !cancelled() && (!inputs.check_only_changed || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }} runs-on: ["self-hosted", "Linux", "X64", "heavy"] - container: "ghcr.io/xrplf/xrpld/nix-debian:sha-8abe82e" + container: "ghcr.io/xrplf/xrpld/nix-debian:sha-63ffdc3" permissions: contents: read issues: write @@ -45,7 +45,7 @@ jobs: uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab + uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8 with: enable_ccache: false diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index 890277d184..0e3f657006 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -68,31 +68,6 @@ jobs: timeout-minutes: 30 steps: - # Packaging runs in a vanilla distro image, so the tooling has to come - # from the distro's archive: debhelper for deb, rpm-build (and the - # systemd / find-debuginfo macros it depends on) for rpm. Run this - # before actions/checkout so the latter can use git (real history) for - # build_pkg.sh's SOURCE_DATE_EPOCH; otherwise it falls back to a tarball - # download and the timestamp comes from wall-clock time. - - name: Install packaging tooling (deb) - if: ${{ matrix.distro == 'debian' }} - run: | - export DEBIAN_FRONTEND=noninteractive - apt-get update - apt-get install -y --no-install-recommends \ - ca-certificates \ - debhelper \ - git - - - name: Install packaging tooling (rpm) - if: ${{ matrix.distro == 'rhel' }} - run: | - dnf install -y --setopt=install_weak_deps=False \ - git \ - rpm-build \ - redhat-rpm-config \ - systemd-rpm-macros - - name: Checkout repository uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 diff --git a/.github/workflows/reusable-upload-recipe.yml b/.github/workflows/reusable-upload-recipe.yml index 6e1ea943ca..1c90fb0e72 100644 --- a/.github/workflows/reusable-upload-recipe.yml +++ b/.github/workflows/reusable-upload-recipe.yml @@ -40,7 +40,7 @@ defaults: jobs: upload: runs-on: ubuntu-latest - container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-8abe82e + container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-63ffdc3 steps: - name: Checkout repository uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index 6310c90899..1a52ceee63 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -67,7 +67,7 @@ jobs: uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Prepare runner - uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab + uses: XRPLF/actions/prepare-runner@c47daebb2f9db64ffbac71b47d68a661498d5ce8 with: enable_ccache: false diff --git a/src/tests/libxrpl/CMakeLists.txt b/src/tests/libxrpl/CMakeLists.txt index 60288e5f20..2dae6fccb9 100644 --- a/src/tests/libxrpl/CMakeLists.txt +++ b/src/tests/libxrpl/CMakeLists.txt @@ -53,4 +53,4 @@ foreach(module IN LISTS test_modules) ) endforeach() -gtest_discover_tests(xrpl_tests) +gtest_discover_tests(xrpl_tests DISCOVERY_TIMEOUT 60) From 577d7457f1e8e9389eb24d49e34ed7cb3b00d28f Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 8 Jun 2026 18:10:05 +0100 Subject: [PATCH 70/87] ci: Use XRPLF/actions build-multiarch-image workflow (#7428) --- .github/workflows/build-nix-images.yml | 7 +- .github/workflows/build-packaging-images.yml | 7 +- .../workflows/reusable-build-docker-image.yml | 89 ------------------- .../reusable-build-merge-docker-images.yml | 89 ------------------- nix/docker/check-tools.sh | 1 + nix/packages.nix | 1 + 6 files changed, 6 insertions(+), 188 deletions(-) delete mode 100644 .github/workflows/reusable-build-docker-image.yml delete mode 100644 .github/workflows/reusable-build-merge-docker-images.yml diff --git a/.github/workflows/build-nix-images.yml b/.github/workflows/build-nix-images.yml index 4e38ca7c57..24f069902d 100644 --- a/.github/workflows/build-nix-images.yml +++ b/.github/workflows/build-nix-images.yml @@ -6,16 +6,12 @@ on: - develop paths: - ".github/workflows/build-nix-images.yml" - - ".github/workflows/reusable-build-docker-image.yml" - - ".github/workflows/reusable-build-merge-docker-images.yml" - "flake.nix" - "flake.lock" - "nix/**" pull_request: paths: - ".github/workflows/build-nix-images.yml" - - ".github/workflows/reusable-build-docker-image.yml" - - ".github/workflows/reusable-build-merge-docker-images.yml" - "flake.nix" - "flake.lock" - "nix/**" @@ -50,8 +46,9 @@ jobs: base_image: debian:bookworm - name: rhel base_image: registry.access.redhat.com/ubi9/ubi:latest - uses: ./.github/workflows/reusable-build-merge-docker-images.yml + uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@c1b480188519e0cad040e6aa70db1cbc5a797e07 with: image_name: ghcr.io/xrplf/xrpld/nix-${{ matrix.distro.name }} dockerfile: nix/docker/Dockerfile base_image: ${{ matrix.distro.base_image }} + push: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} diff --git a/.github/workflows/build-packaging-images.yml b/.github/workflows/build-packaging-images.yml index c445dbf726..d6dabb0f95 100644 --- a/.github/workflows/build-packaging-images.yml +++ b/.github/workflows/build-packaging-images.yml @@ -6,15 +6,11 @@ on: - develop paths: - ".github/workflows/build-packaging-images.yml" - - ".github/workflows/reusable-build-docker-image.yml" - - ".github/workflows/reusable-build-merge-docker-images.yml" - "package/Dockerfile" - "package/install-packaging-tools.sh" pull_request: paths: - ".github/workflows/build-packaging-images.yml" - - ".github/workflows/reusable-build-docker-image.yml" - - ".github/workflows/reusable-build-merge-docker-images.yml" - "package/Dockerfile" - "package/install-packaging-tools.sh" workflow_dispatch: @@ -42,8 +38,9 @@ jobs: base_image: debian:bookworm - name: rhel base_image: registry.access.redhat.com/ubi9/ubi:latest - uses: ./.github/workflows/reusable-build-merge-docker-images.yml + uses: XRPLF/actions/.github/workflows/build-multiarch-image.yml@c1b480188519e0cad040e6aa70db1cbc5a797e07 with: image_name: ghcr.io/xrplf/xrpld/packaging-${{ matrix.distro.name }} dockerfile: package/Dockerfile base_image: ${{ matrix.distro.base_image }} + push: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} diff --git a/.github/workflows/reusable-build-docker-image.yml b/.github/workflows/reusable-build-docker-image.yml deleted file mode 100644 index 253563c6a5..0000000000 --- a/.github/workflows/reusable-build-docker-image.yml +++ /dev/null @@ -1,89 +0,0 @@ -# Build a single-platform Docker image. On push, the image is pushed to -# GHCR with arch-suffixed tags (e.g. `:latest-amd64`, `:sha-abc-amd64`) -# so the calling workflow can stitch per-arch builds into a multi-arch -# manifest without needing to pass digests around. -name: Reusable build Docker image (single platform) - -on: - workflow_call: - inputs: - image_name: - description: "Full image name without tag (e.g. 'ghcr.io/xrplf/xrpld/nix-ubuntu')" - required: true - type: string - dockerfile: - description: "Path to the Dockerfile, relative to the repository root" - required: true - type: string - base_image: - description: "Value passed to the Dockerfile as the BASE_IMAGE build arg" - required: true - type: string - platform: - description: "Docker platform string, e.g. linux/amd64" - required: true - type: string - runner: - description: "GitHub Actions runner label to build on" - required: true - type: string - push: - description: "Whether to push the image to GHCR" - required: true - type: boolean - -defaults: - run: - shell: bash - -jobs: - build: - name: Build ${{ inputs.platform }} - runs-on: ${{ inputs.runner }} - permissions: - contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Determine arch - id: vars - env: - PLATFORM: ${{ inputs.platform }} - run: | - echo "arch=${PLATFORM##*/}" >>$GITHUB_OUTPUT - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - - name: Login to GitHub Container Registry - if: inputs.push - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Docker metadata - id: meta - uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 - with: - images: ${{ inputs.image_name }} - tags: | - type=sha,prefix=sha-,format=short - type=raw,value=latest - flavor: | - suffix=-${{ steps.vars.outputs.arch }},onlatest=true - - - name: Build and push - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 - with: - context: . - file: ${{ inputs.dockerfile }} - platforms: ${{ inputs.platform }} - push: ${{ inputs.push }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: BASE_IMAGE=${{ inputs.base_image }} diff --git a/.github/workflows/reusable-build-merge-docker-images.yml b/.github/workflows/reusable-build-merge-docker-images.yml deleted file mode 100644 index 98deb6ea3f..0000000000 --- a/.github/workflows/reusable-build-merge-docker-images.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: Reusable build and merge Docker image (multi-arch) - -on: - workflow_call: - inputs: - image_name: - description: "Full image name without tag (e.g. 'ghcr.io/xrplf/xrpld/nix-ubuntu')" - required: true - type: string - dockerfile: - description: "Path to the Dockerfile, relative to the repository root" - required: true - type: string - base_image: - description: "Value passed to the Dockerfile as the BASE_IMAGE build arg" - required: true - type: string - -defaults: - run: - shell: bash - -jobs: - build: - name: Build ${{ inputs.image_name }} - permissions: - contents: read - packages: write - - strategy: - fail-fast: false - matrix: - target: - - platform: linux/amd64 - runner: ubuntu-latest - - platform: linux/arm64 - runner: ubuntu-24.04-arm - - uses: ./.github/workflows/reusable-build-docker-image.yml - with: - image_name: ${{ inputs.image_name }} - dockerfile: ${{ inputs.dockerfile }} - base_image: ${{ inputs.base_image }} - platform: ${{ matrix.target.platform }} - runner: ${{ matrix.target.runner }} - push: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} - - merge: - name: Merge ${{ inputs.image_name }} - needs: build - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - - name: Docker metadata - id: meta - uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 - with: - images: ${{ inputs.image_name }} - tags: | - type=sha,prefix=sha-,format=short - type=raw,value=latest - - - name: Login to GitHub Container Registry - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create multi-arch manifests - if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} - run: | - for tag in $(jq -cr '.tags[]' <<<"$DOCKER_METADATA_OUTPUT_JSON"); do - docker buildx imagetools create -t "$tag" "${tag}-amd64" "${tag}-arm64" - done - - - name: Inspect image - if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' }} - env: - IMAGE_NAME: ${{ inputs.image_name }} - IMAGE_VERSION: ${{ steps.meta.outputs.version }} - run: | - docker buildx imagetools inspect "${IMAGE_NAME}:${IMAGE_VERSION}" diff --git a/nix/docker/check-tools.sh b/nix/docker/check-tools.sh index faa6520678..67bcdff8a9 100755 --- a/nix/docker/check-tools.sh +++ b/nix/docker/check-tools.sh @@ -15,6 +15,7 @@ gcc --version gcov --version gcovr --version git --version +git-cliff --version gpg --version less --version make --version diff --git a/nix/packages.nix b/nix/packages.nix index 6a83446d88..d40472634b 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -15,6 +15,7 @@ in doxygen gcovr git + git-cliff gnumake gnupg # needed for signing commits & codecov/codecov-action llvmPackages_22.clang-tools From ee9fbc4e08cd52afed4bf3abd3e2c46169331896 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 9 Jun 2026 06:04:09 -0400 Subject: [PATCH 71/87] refactor: Use const function arguments where possible (#7423) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- include/xrpl/net/HTTPClient.h | 6 ++--- include/xrpl/shamap/SHAMap.h | 24 +++++++++++-------- src/libxrpl/net/HTTPClient.cpp | 8 +++---- src/libxrpl/shamap/SHAMap.cpp | 10 ++++---- src/libxrpl/shamap/SHAMapSync.cpp | 6 ++--- src/xrpld/app/ledger/InboundLedger.h | 8 +++---- src/xrpld/app/ledger/detail/InboundLedger.cpp | 10 ++++---- src/xrpld/app/main/Application.cpp | 4 ++-- src/xrpld/app/main/Application.h | 2 +- src/xrpld/consensus/Validations.h | 2 +- src/xrpld/overlay/Overlay.h | 8 +++---- src/xrpld/overlay/detail/OverlayImpl.cpp | 10 ++++---- src/xrpld/overlay/detail/OverlayImpl.h | 10 ++++---- 13 files changed, 56 insertions(+), 52 deletions(-) diff --git a/include/xrpl/net/HTTPClient.h b/include/xrpl/net/HTTPClient.h index f059b19047..456f769922 100644 --- a/include/xrpl/net/HTTPClient.h +++ b/include/xrpl/net/HTTPClient.h @@ -53,7 +53,7 @@ public: boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> complete, - beast::Journal& j); + beast::Journal const& j); static void get(bool bSSL, @@ -67,7 +67,7 @@ public: boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> complete, - beast::Journal& j); + beast::Journal const& j); static void request( @@ -82,7 +82,7 @@ public: boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> complete, - beast::Journal& j); + beast::Journal const& j); }; } // namespace xrpl diff --git a/include/xrpl/shamap/SHAMap.h b/include/xrpl/shamap/SHAMap.h index 32e87b64c6..3d08318cf6 100644 --- a/include/xrpl/shamap/SHAMap.h +++ b/include/xrpl/shamap/SHAMap.h @@ -161,7 +161,7 @@ public: setLedgerSeq(std::uint32_t lseq); bool - fetchRoot(SHAMapHash const& hash, SHAMapSyncFilter* filter); + fetchRoot(SHAMapHash const& hash, SHAMapSyncFilter const* filter); // normal hash access functions @@ -248,7 +248,7 @@ public: @param return The nodes known to be missing */ std::vector> - getMissingNodes(int maxNodes, SHAMapSyncFilter* filter); + getMissingNodes(int maxNodes, SHAMapSyncFilter const* filter); bool getNodeFat( @@ -281,9 +281,9 @@ public: serializeRoot(Serializer& s) const; SHAMapAddNode - addRootNode(SHAMapHash const& hash, Slice const& rootNode, SHAMapSyncFilter* filter); + addRootNode(SHAMapHash const& hash, Slice const& rootNode, SHAMapSyncFilter const* filter); SHAMapAddNode - addKnownNode(SHAMapNodeID const& nodeID, Slice const& rawNode, SHAMapSyncFilter* filter); + addKnownNode(SHAMapNodeID const& nodeID, Slice const& rawNode, SHAMapSyncFilter const* filter); // status functions void @@ -343,11 +343,11 @@ private: SHAMapTreeNodePtr fetchNodeNT(SHAMapHash const& hash) const; SHAMapTreeNodePtr - fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const; + fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter const* filter) const; SHAMapTreeNodePtr fetchNode(SHAMapHash const& hash) const; SHAMapTreeNodePtr - checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const; + checkFilter(SHAMapHash const& hash, SHAMapSyncFilter const* filter) const; /** Update hashes up to the root */ void @@ -411,7 +411,7 @@ private: descendAsync( SHAMapInnerNode* parent, int branch, - SHAMapSyncFilter* filter, + SHAMapSyncFilter const* filter, bool& pending, descendCallback&&) const; @@ -420,7 +420,7 @@ private: SHAMapInnerNode* parent, SHAMapNodeID const& parentID, int branch, - SHAMapSyncFilter* filter) const; + SHAMapSyncFilter const* filter) const; // Non-storing // Does not hook the returned node to its parent @@ -461,7 +461,7 @@ private: // basic parameters int max; - SHAMapSyncFilter* filter; + SHAMapSyncFilter const* filter; int const maxDefer; std::uint32_t generation; @@ -500,7 +500,11 @@ private: // reads std::map resumes; - MissingNodes(int max, SHAMapSyncFilter* filter, int maxDefer, std::uint32_t generation) + MissingNodes( + int max, + SHAMapSyncFilter const* filter, + int maxDefer, + std::uint32_t generation) : max(max), filter(filter), maxDefer(maxDefer), generation(generation), deferred(0) { missingNodes.reserve(max); diff --git a/src/libxrpl/net/HTTPClient.cpp b/src/libxrpl/net/HTTPClient.cpp index 78ee5eb577..4b9cc9d6e6 100644 --- a/src/libxrpl/net/HTTPClient.cpp +++ b/src/libxrpl/net/HTTPClient.cpp @@ -64,7 +64,7 @@ public: boost::asio::io_context& ioContext, unsigned short const port, std::size_t maxResponseSize, - beast::Journal& j) + beast::Journal const& j) : socket_( ioContext, gHttpClientSslContext->context()) // NOLINT(bugprone-unchecked-optional-access) @@ -552,7 +552,7 @@ HTTPClient::get( std::function< bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> complete, - beast::Journal& j) + beast::Journal const& j) { auto client = std::make_shared(ioContext, port, responseMax, j); client->get(bSSL, deqSites, strPath, timeout, complete); @@ -570,7 +570,7 @@ HTTPClient::get( std::function< bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> complete, - beast::Journal& j) + beast::Journal const& j) { std::deque const deqSites(1, strSite); @@ -590,7 +590,7 @@ HTTPClient::request( std::function< bool(boost::system::error_code const& ecResult, int iStatus, std::string const& strData)> complete, - beast::Journal& j) + beast::Journal const& j) { std::deque const deqSites(1, strSite); diff --git a/src/libxrpl/shamap/SHAMap.cpp b/src/libxrpl/shamap/SHAMap.cpp index 4aad255d81..8a521f6a47 100644 --- a/src/libxrpl/shamap/SHAMap.cpp +++ b/src/libxrpl/shamap/SHAMap.cpp @@ -206,7 +206,7 @@ SHAMap::finishFetch(SHAMapHash const& hash, std::shared_ptr const& o // See if a sync filter has a node SHAMapTreeNodePtr -SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const +SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter const* filter) const { if (auto nodeData = filter->getNode(hash)) { @@ -232,7 +232,7 @@ SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const // Get a node without throwing // Used on maps where missing nodes are expected SHAMapTreeNodePtr -SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const +SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter const* filter) const { auto node = cacheLookup(hash); if (node) @@ -345,7 +345,7 @@ SHAMap::descend( SHAMapInnerNode* parent, SHAMapNodeID const& parentID, int branch, - SHAMapSyncFilter* filter) const + SHAMapSyncFilter const* filter) const { XRPL_ASSERT(parent->isInner(), "xrpl::SHAMap::descend : valid parent input"); XRPL_ASSERT( @@ -374,7 +374,7 @@ SHAMapTreeNode* SHAMap::descendAsync( SHAMapInnerNode* parent, int branch, - SHAMapSyncFilter* filter, + SHAMapSyncFilter const* filter, bool& pending, descendCallback&& callback) const { @@ -885,7 +885,7 @@ SHAMap::updateGiveItem(SHAMapNodeType type, boost::intrusive_ptrgetHash()) return true; diff --git a/src/libxrpl/shamap/SHAMapSync.cpp b/src/libxrpl/shamap/SHAMapSync.cpp index 0601bfefda..1f38049abe 100644 --- a/src/libxrpl/shamap/SHAMapSync.cpp +++ b/src/libxrpl/shamap/SHAMapSync.cpp @@ -305,7 +305,7 @@ SHAMap::gmnProcessDeferredReads(MissingNodes& mn) nodes that are not permanently stored locally */ std::vector> -SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter) +SHAMap::getMissingNodes(int max, SHAMapSyncFilter const* filter) { XRPL_ASSERT(root_->getHash().isNonZero(), "xrpl::SHAMap::getMissingNodes : nonzero root hash"); XRPL_ASSERT(max > 0, "xrpl::SHAMap::getMissingNodes : valid max input"); @@ -507,7 +507,7 @@ SHAMap::serializeRoot(Serializer& s) const } SHAMapAddNode -SHAMap::addRootNode(SHAMapHash const& hash, Slice const& rootNode, SHAMapSyncFilter* filter) +SHAMap::addRootNode(SHAMapHash const& hash, Slice const& rootNode, SHAMapSyncFilter const* filter) { // we already have a root_ node if (root_->getHash().isNonZero()) @@ -542,7 +542,7 @@ SHAMap::addRootNode(SHAMapHash const& hash, Slice const& rootNode, SHAMapSyncFil } SHAMapAddNode -SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncFilter* filter) +SHAMap::addKnownNode(SHAMapNodeID const& node, Slice const& rawNode, SHAMapSyncFilter const* filter) { XRPL_ASSERT(!node.isRoot(), "xrpl::SHAMap::addKnownNode : valid node input"); diff --git a/src/xrpld/app/ledger/InboundLedger.h b/src/xrpld/app/ledger/InboundLedger.h index d155c5902c..b82e2f69cd 100644 --- a/src/xrpld/app/ledger/InboundLedger.h +++ b/src/xrpld/app/ledger/InboundLedger.h @@ -128,13 +128,13 @@ private: pmDowncast() override; int - processData(std::shared_ptr peer, protocol::TMLedgerData& data); + processData(std::shared_ptr peer, protocol::TMLedgerData const& data); bool takeHeader(std::string const& data); void - receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode&); + receiveNode(protocol::TMLedgerData const& packet, SHAMapAddNode&); bool takeTxRootNode(Slice const& data, SHAMapAddNode&); @@ -143,10 +143,10 @@ private: takeAsRootNode(Slice const& data, SHAMapAddNode&); std::vector - neededTxHashes(int max, SHAMapSyncFilter* filter) const; + neededTxHashes(int max, SHAMapSyncFilter const* filter) const; std::vector - neededStateHashes(int max, SHAMapSyncFilter* filter) const; + neededStateHashes(int max, SHAMapSyncFilter const* filter) const; clock_type& clock_; clock_type::time_point lastAction_; diff --git a/src/xrpld/app/ledger/detail/InboundLedger.cpp b/src/xrpld/app/ledger/detail/InboundLedger.cpp index 9ba7bdf22e..5a9f24cc2e 100644 --- a/src/xrpld/app/ledger/detail/InboundLedger.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedger.cpp @@ -188,7 +188,7 @@ InboundLedger::~InboundLedger() } static std::vector -neededHashes(uint256 const& root, SHAMap& map, int max, SHAMapSyncFilter* filter) +neededHashes(uint256 const& root, SHAMap& map, int max, SHAMapSyncFilter const* filter) { std::vector ret; @@ -211,13 +211,13 @@ neededHashes(uint256 const& root, SHAMap& map, int max, SHAMapSyncFilter* filter } std::vector -InboundLedger::neededTxHashes(int max, SHAMapSyncFilter* filter) const +InboundLedger::neededTxHashes(int max, SHAMapSyncFilter const* filter) const { return neededHashes(ledger_->header().txHash, ledger_->txMap(), max, filter); } std::vector -InboundLedger::neededStateHashes(int max, SHAMapSyncFilter* filter) const +InboundLedger::neededStateHashes(int max, SHAMapSyncFilter const* filter) const { return neededHashes(ledger_->header().accountHash, ledger_->stateMap(), max, filter); } @@ -820,7 +820,7 @@ InboundLedger::takeHeader(std::string const& data) Call with a lock */ void -InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san) +InboundLedger::receiveNode(protocol::TMLedgerData const& packet, SHAMapAddNode& san) { if (!haveHeader_) { @@ -1026,7 +1026,7 @@ InboundLedger::gotData( // TODO Change peer to Consumer // int -InboundLedger::processData(std::shared_ptr peer, protocol::TMLedgerData& packet) +InboundLedger::processData(std::shared_ptr peer, protocol::TMLedgerData const& packet) { if (packet.type() == protocol::liBASE) { diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index 508dfc8590..af5d51289d 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -492,7 +492,7 @@ public: void run() override; void - signalStop(std::string msg) override; + signalStop(std::string const& msg) override; bool checkSigs() const override; void @@ -1602,7 +1602,7 @@ ApplicationImp::run() } void -ApplicationImp::signalStop(std::string msg) +ApplicationImp::signalStop(std::string const& msg) { if (!isTimeToStop.test_and_set(std::memory_order_acquire)) { diff --git a/src/xrpld/app/main/Application.h b/src/xrpld/app/main/Application.h index 200fed7cf9..08e41e2c4c 100644 --- a/src/xrpld/app/main/Application.h +++ b/src/xrpld/app/main/Application.h @@ -111,7 +111,7 @@ public: virtual void run() = 0; virtual void - signalStop(std::string msg) = 0; + signalStop(std::string const& msg) = 0; [[nodiscard]] virtual bool checkSigs() const = 0; virtual void diff --git a/src/xrpld/consensus/Validations.h b/src/xrpld/consensus/Validations.h index 7be578060e..2f5762ce83 100644 --- a/src/xrpld/consensus/Validations.h +++ b/src/xrpld/consensus/Validations.h @@ -693,7 +693,7 @@ public: validationSET_EXPIRES ago and were not asked to keep. */ void - expire(beast::Journal& j) + expire(beast::Journal const& j) { auto const start = std::chrono::steady_clock::now(); { diff --git a/src/xrpld/overlay/Overlay.h b/src/xrpld/overlay/Overlay.h index ef97ea7f24..87c6ff132a 100644 --- a/src/xrpld/overlay/Overlay.h +++ b/src/xrpld/overlay/Overlay.h @@ -117,11 +117,11 @@ public: /** Broadcast a proposal. */ virtual void - broadcast(protocol::TMProposeSet& m) = 0; + broadcast(protocol::TMProposeSet const& m) = 0; /** Broadcast a validation. */ virtual void - broadcast(protocol::TMValidation& m) = 0; + broadcast(protocol::TMValidation const& m) = 0; /** Relay a proposal. * @param m the serialized proposal @@ -130,7 +130,7 @@ public: * @return the set of peers which have already sent us this proposal */ virtual std::set - relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey const& validator) = 0; + relay(protocol::TMProposeSet const& m, uint256 const& uid, PublicKey const& validator) = 0; /** Relay a validation. * @param m the serialized validation @@ -139,7 +139,7 @@ public: * @return the set of peers which have already sent us this validation */ virtual std::set - relay(protocol::TMValidation& m, uint256 const& uid, PublicKey const& validator) = 0; + relay(protocol::TMValidation const& m, uint256 const& uid, PublicKey const& validator) = 0; /** Relay a transaction. If the tx reduce-relay feature is enabled then * randomly select peers to relay to and queue transaction's hash diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index b31f54058a..b71cef6719 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -407,7 +407,7 @@ OverlayImpl::makeErrorResponse( std::shared_ptr const& slot, http_request_type const& request, address_type remoteAddress, - std::string text) + std::string const& text) { boost::beast::http::response msg; msg.version(request.version()); @@ -1157,14 +1157,14 @@ OverlayImpl::findPeerByPublicKey(PublicKey const& pubKey) } void -OverlayImpl::broadcast(protocol::TMProposeSet& m) +OverlayImpl::broadcast(protocol::TMProposeSet const& m) { auto const sm = std::make_shared(m, protocol::mtPROPOSE_LEDGER); forEach([&](std::shared_ptr const& p) { p->send(sm); }); } std::set -OverlayImpl::relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey const& validator) +OverlayImpl::relay(protocol::TMProposeSet const& m, uint256 const& uid, PublicKey const& validator) { if (auto const toSkip = app_.getHashRouter().shouldRelay(uid)) { @@ -1179,14 +1179,14 @@ OverlayImpl::relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey cons } void -OverlayImpl::broadcast(protocol::TMValidation& m) +OverlayImpl::broadcast(protocol::TMValidation const& m) { auto const sm = std::make_shared(m, protocol::mtVALIDATION); forEach([sm](std::shared_ptr const& p) { p->send(sm); }); } std::set -OverlayImpl::relay(protocol::TMValidation& m, uint256 const& uid, PublicKey const& validator) +OverlayImpl::relay(protocol::TMValidation const& m, uint256 const& uid, PublicKey const& validator) { if (auto const toSkip = app_.getHashRouter().shouldRelay(uid)) { diff --git a/src/xrpld/overlay/detail/OverlayImpl.h b/src/xrpld/overlay/detail/OverlayImpl.h index 6fcc2df854..545d9eb75c 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.h +++ b/src/xrpld/overlay/detail/OverlayImpl.h @@ -202,16 +202,16 @@ public: findPeerByPublicKey(PublicKey const& pubKey) override; void - broadcast(protocol::TMProposeSet& m) override; + broadcast(protocol::TMProposeSet const& m) override; void - broadcast(protocol::TMValidation& m) override; + broadcast(protocol::TMValidation const& m) override; std::set - relay(protocol::TMProposeSet& m, uint256 const& uid, PublicKey const& validator) override; + relay(protocol::TMProposeSet const& m, uint256 const& uid, PublicKey const& validator) override; std::set - relay(protocol::TMValidation& m, uint256 const& uid, PublicKey const& validator) override; + relay(protocol::TMValidation const& m, uint256 const& uid, PublicKey const& validator) override; void relay( @@ -433,7 +433,7 @@ private: std::shared_ptr const& slot, http_request_type const& request, address_type remoteAddress, - std::string msg); + std::string const& msg); /** Handles crawl requests. Crawl returns information about the node and its peers so crawlers can map the network. From c9769d1add290e4fb53add23d1e9859237134b73 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 9 Jun 2026 09:56:32 -0400 Subject: [PATCH 72/87] refactor: Use `std::move` and `std::string_view` where possible (#7424) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- include/xrpl/basics/StringUtilities.h | 9 ++---- include/xrpl/basics/base64.h | 3 +- include/xrpl/basics/join.h | 3 +- include/xrpl/beast/insight/Counter.h | 3 +- include/xrpl/beast/insight/Event.h | 3 +- include/xrpl/beast/insight/Gauge.h | 3 +- include/xrpl/beast/insight/Hook.h | 3 +- include/xrpl/beast/insight/Meter.h | 3 +- include/xrpl/beast/unit_test/match.h | 6 ++-- include/xrpl/crypto/RFC1751.h | 3 +- include/xrpl/ledger/CanonicalTXSet.h | 2 +- include/xrpl/protocol/STVector256.h | 10 +++--- include/xrpl/server/Session.h | 5 +-- include/xrpl/shamap/SHAMapNodeID.h | 3 +- src/libxrpl/beast/insight/StatsDCollector.cpp | 32 +++++++++---------- src/libxrpl/crypto/RFC1751.cpp | 3 +- src/libxrpl/ledger/CanonicalTXSet.cpp | 10 ++---- src/test/jtx/credentials.h | 2 +- src/test/server/Server_test.cpp | 4 ++- src/xrpld/app/consensus/RCLCxLedger.h | 2 +- src/xrpld/app/consensus/RCLValidations.h | 2 +- src/xrpld/app/ledger/AcceptedLedger.cpp | 19 ++++------- src/xrpld/app/ledger/AcceptedLedger.h | 2 +- src/xrpld/app/ledger/OpenLedger.h | 3 +- src/xrpld/app/ledger/detail/OpenLedger.cpp | 3 +- src/xrpld/app/ledger/detail/SkipListAcquire.h | 4 +-- src/xrpld/app/misc/detail/ValidatorList.cpp | 2 +- src/xrpld/overlay/detail/Cluster.cpp | 3 +- src/xrpld/peerfinder/PeerfinderManager.h | 2 +- src/xrpld/peerfinder/detail/Logic.h | 4 +-- .../peerfinder/detail/PeerfinderManager.cpp | 4 +-- src/xrpld/rpc/detail/AssetCache.cpp | 4 +-- src/xrpld/rpc/detail/AssetCache.h | 2 +- src/xrpld/rpc/detail/PathRequest.cpp | 4 +-- src/xrpld/rpc/detail/PathRequest.h | 2 +- 35 files changed, 86 insertions(+), 86 deletions(-) diff --git a/include/xrpl/basics/StringUtilities.h b/include/xrpl/basics/StringUtilities.h index 28421626aa..1d3434b7ed 100644 --- a/include/xrpl/basics/StringUtilities.h +++ b/include/xrpl/basics/StringUtilities.h @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace xrpl { @@ -95,13 +96,7 @@ strUnHex(std::size_t strSize, Iterator begin, Iterator end) } inline std::optional -strUnHex(std::string const& strSrc) -{ - return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend()); -} - -inline std::optional -strViewUnHex(std::string_view strSrc) +strUnHex(std::string_view strSrc) { return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend()); } diff --git a/include/xrpl/basics/base64.h b/include/xrpl/basics/base64.h index ed30e40a36..660958ce14 100644 --- a/include/xrpl/basics/base64.h +++ b/include/xrpl/basics/base64.h @@ -36,6 +36,7 @@ #include #include +#include namespace xrpl { @@ -43,7 +44,7 @@ std::string base64Encode(std::uint8_t const* data, std::size_t len); inline std::string -base64Encode(std::string const& s) +base64Encode(std::string_view s) { return base64Encode(reinterpret_cast(s.data()), s.size()); } diff --git a/include/xrpl/basics/join.h b/include/xrpl/basics/join.h index 0fb00aaf82..c214212473 100644 --- a/include/xrpl/basics/join.h +++ b/include/xrpl/basics/join.h @@ -1,12 +1,13 @@ #pragma once #include +#include namespace xrpl { template Stream& -join(Stream& s, Iter iter, Iter end, std::string const& delimiter) +join(Stream& s, Iter iter, Iter end, std::string_view delimiter) { if (iter == end) return s; diff --git a/include/xrpl/beast/insight/Counter.h b/include/xrpl/beast/insight/Counter.h index 71ace4bb4e..482808b2c7 100644 --- a/include/xrpl/beast/insight/Counter.h +++ b/include/xrpl/beast/insight/Counter.h @@ -3,6 +3,7 @@ #include #include +#include namespace beast::insight { @@ -29,7 +30,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Counter(std::shared_ptr const& impl) : impl_(impl) + explicit Counter(std::shared_ptr impl) : impl_(std::move(impl)) { } diff --git a/include/xrpl/beast/insight/Event.h b/include/xrpl/beast/insight/Event.h index 5e424a0f9b..afccf9baba 100644 --- a/include/xrpl/beast/insight/Event.h +++ b/include/xrpl/beast/insight/Event.h @@ -4,6 +4,7 @@ #include #include +#include namespace beast::insight { @@ -31,7 +32,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Event(std::shared_ptr const& impl) : impl_(impl) + explicit Event(std::shared_ptr impl) : impl_(std::move(impl)) { } diff --git a/include/xrpl/beast/insight/Gauge.h b/include/xrpl/beast/insight/Gauge.h index dd2c4bc6b6..b24c4366c3 100644 --- a/include/xrpl/beast/insight/Gauge.h +++ b/include/xrpl/beast/insight/Gauge.h @@ -3,6 +3,7 @@ #include #include +#include namespace beast::insight { @@ -31,7 +32,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Gauge(std::shared_ptr const& impl) : impl_(impl) + explicit Gauge(std::shared_ptr impl) : impl_(std::move(impl)) { } diff --git a/include/xrpl/beast/insight/Hook.h b/include/xrpl/beast/insight/Hook.h index 1cb6cae5d9..8dbe5a4be0 100644 --- a/include/xrpl/beast/insight/Hook.h +++ b/include/xrpl/beast/insight/Hook.h @@ -3,6 +3,7 @@ #include #include +#include namespace beast::insight { @@ -20,7 +21,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Hook(std::shared_ptr const& impl) : impl_(impl) + 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 03aa17c313..25ffabd928 100644 --- a/include/xrpl/beast/insight/Meter.h +++ b/include/xrpl/beast/insight/Meter.h @@ -3,6 +3,7 @@ #include #include +#include namespace beast::insight { @@ -28,7 +29,7 @@ public: factory function in the Collector interface. @see Collector. */ - explicit Meter(std::shared_ptr const& impl) : impl_(impl) + explicit Meter(std::shared_ptr impl) : impl_(std::move(impl)) { } diff --git a/include/xrpl/beast/unit_test/match.h b/include/xrpl/beast/unit_test/match.h index 222c4ea656..da466ab228 100644 --- a/include/xrpl/beast/unit_test/match.h +++ b/include/xrpl/beast/unit_test/match.h @@ -41,7 +41,7 @@ private: public: template - explicit Selector(ModeT mode, std::string const& pattern = ""); + explicit Selector(ModeT mode, std::string pattern = ""); template bool @@ -51,9 +51,9 @@ public: //------------------------------------------------------------------------------ template -Selector::Selector(ModeT mode, std::string const& pattern) : mode_(mode), pat_(pattern) +Selector::Selector(ModeT mode, std::string pattern) : mode_(mode), pat_(std::move(pattern)) { - if (mode_ == ModeT::Automatch && pattern.empty()) + if (mode_ == ModeT::Automatch && pat_.empty()) mode_ = ModeT::All; } diff --git a/include/xrpl/crypto/RFC1751.h b/include/xrpl/crypto/RFC1751.h index c99c691ba0..19b636b9dc 100644 --- a/include/xrpl/crypto/RFC1751.h +++ b/include/xrpl/crypto/RFC1751.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace xrpl { @@ -34,7 +35,7 @@ private: static void standard(std::string& strWord); static int - wsrch(std::string const& strWord, int iMin, int iMax); + wsrch(std::string_view strWord, int iMin, int iMax); static int etob(std::string& strData, std::vector vsHuman); diff --git a/include/xrpl/ledger/CanonicalTXSet.h b/include/xrpl/ledger/CanonicalTXSet.h index 8653816eee..4dffadd52f 100644 --- a/include/xrpl/ledger/CanonicalTXSet.h +++ b/include/xrpl/ledger/CanonicalTXSet.h @@ -93,7 +93,7 @@ public: } void - insert(std::shared_ptr const& txn); + insert(std::shared_ptr txn); // Pops the next transaction on account that follows seqProx in the // sort order. Normally called when a transaction is successfully diff --git a/include/xrpl/protocol/STVector256.h b/include/xrpl/protocol/STVector256.h index ab3a2f99e3..5c454b6be0 100644 --- a/include/xrpl/protocol/STVector256.h +++ b/include/xrpl/protocol/STVector256.h @@ -17,8 +17,8 @@ public: STVector256() = default; explicit STVector256(SField const& n); - explicit STVector256(std::vector const& vector); - STVector256(SField const& n, std::vector const& vector); + explicit STVector256(std::vector vector); + STVector256(SField const& n, std::vector vector); STVector256(SerialIter& sit, SField const& name); [[nodiscard]] SerializedTypeID @@ -103,12 +103,12 @@ inline STVector256::STVector256(SField const& n) : STBase(n) { } -inline STVector256::STVector256(std::vector const& vector) : value_(vector) +inline STVector256::STVector256(std::vector vector) : value_(std::move(vector)) { } -inline STVector256::STVector256(SField const& n, std::vector const& vector) - : STBase(n), value_(vector) +inline STVector256::STVector256(SField const& n, std::vector vector) + : STBase(n), value_(std::move(vector)) { } diff --git a/include/xrpl/server/Session.h b/include/xrpl/server/Session.h index 2cb991e130..151e57e7f2 100644 --- a/include/xrpl/server/Session.h +++ b/include/xrpl/server/Session.h @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace xrpl { @@ -53,10 +54,10 @@ public: /** Send a copy of data asynchronously. */ /** @{ */ void - write(std::string const& s) + write(std::string_view s) { if (!s.empty()) - write(&s[0], std::distance(s.begin(), s.end())); + write(s.data(), s.size()); } template diff --git a/include/xrpl/shamap/SHAMapNodeID.h b/include/xrpl/shamap/SHAMapNodeID.h index dbc087b356..248c9cb80b 100644 --- a/include/xrpl/shamap/SHAMapNodeID.h +++ b/include/xrpl/shamap/SHAMapNodeID.h @@ -5,6 +5,7 @@ #include #include +#include #include namespace xrpl { @@ -127,7 +128,7 @@ operator<<(std::ostream& out, SHAMapNodeID const& node) deserializeSHAMapNodeID(void const* data, std::size_t size); [[nodiscard]] inline std::optional -deserializeSHAMapNodeID(std::string const& s) +deserializeSHAMapNodeID(std::string_view s) { return deserializeSHAMapNodeID(s.data(), s.size()); } diff --git a/src/libxrpl/beast/insight/StatsDCollector.cpp b/src/libxrpl/beast/insight/StatsDCollector.cpp index 88f52c26de..0d0e013274 100644 --- a/src/libxrpl/beast/insight/StatsDCollector.cpp +++ b/src/libxrpl/beast/insight/StatsDCollector.cpp @@ -66,7 +66,7 @@ public: class StatsDHookImpl : public HookImpl, public StatsDMetricBase { public: - StatsDHookImpl(HandlerType handler, std::shared_ptr const& impl); + StatsDHookImpl(HandlerType handler, std::shared_ptr impl); ~StatsDHookImpl() override; @@ -86,7 +86,7 @@ private: class StatsDCounterImpl : public CounterImpl, public StatsDMetricBase { public: - StatsDCounterImpl(std::string name, std::shared_ptr const& impl); + StatsDCounterImpl(std::string name, std::shared_ptr impl); ~StatsDCounterImpl() override; @@ -115,7 +115,7 @@ private: class StatsDEventImpl : public EventImpl { public: - StatsDEventImpl(std::string name, std::shared_ptr const& impl); + StatsDEventImpl(std::string name, std::shared_ptr impl); ~StatsDEventImpl() override = default; @@ -140,7 +140,7 @@ private: class StatsDGaugeImpl : public GaugeImpl, public StatsDMetricBase { public: - StatsDGaugeImpl(std::string name, std::shared_ptr const& impl); + StatsDGaugeImpl(std::string name, std::shared_ptr impl); ~StatsDGaugeImpl() override; @@ -174,7 +174,7 @@ private: class StatsDMeterImpl : public MeterImpl, public StatsDMetricBase { public: - explicit StatsDMeterImpl(std::string name, std::shared_ptr const& impl); + explicit StatsDMeterImpl(std::string name, std::shared_ptr impl); ~StatsDMeterImpl() override; @@ -478,8 +478,8 @@ public: //------------------------------------------------------------------------------ -StatsDHookImpl::StatsDHookImpl(HandlerType handler, std::shared_ptr const& impl) - : impl_(impl), handler_(std::move(handler)) +StatsDHookImpl::StatsDHookImpl(HandlerType handler, std::shared_ptr impl) + : impl_(std::move(impl)), handler_(std::move(handler)) { impl_->add(*this); } @@ -497,10 +497,8 @@ StatsDHookImpl::doProcess() //------------------------------------------------------------------------------ -StatsDCounterImpl::StatsDCounterImpl( - std::string name, - std::shared_ptr const& impl) - : impl_(impl), name_(std::move(name)) +StatsDCounterImpl::StatsDCounterImpl(std::string name, std::shared_ptr impl) + : impl_(std::move(impl)), name_(std::move(name)) { impl_->add(*this); } @@ -550,8 +548,8 @@ StatsDCounterImpl::doProcess() //------------------------------------------------------------------------------ -StatsDEventImpl::StatsDEventImpl(std::string name, std::shared_ptr const& impl) - : impl_(impl), name_(std::move(name)) +StatsDEventImpl::StatsDEventImpl(std::string name, std::shared_ptr impl) + : impl_(std::move(impl)), name_(std::move(name)) { } @@ -577,8 +575,8 @@ StatsDEventImpl::doNotify(EventImpl::value_type const& value) //------------------------------------------------------------------------------ -StatsDGaugeImpl::StatsDGaugeImpl(std::string name, std::shared_ptr const& impl) - : impl_(impl), name_(std::move(name)) +StatsDGaugeImpl::StatsDGaugeImpl(std::string name, std::shared_ptr impl) + : impl_(std::move(impl)), name_(std::move(name)) { impl_->add(*this); } @@ -664,8 +662,8 @@ StatsDGaugeImpl::doProcess() //------------------------------------------------------------------------------ -StatsDMeterImpl::StatsDMeterImpl(std::string name, std::shared_ptr const& impl) - : impl_(impl), name_(std::move(name)) +StatsDMeterImpl::StatsDMeterImpl(std::string name, std::shared_ptr impl) + : impl_(std::move(impl)), name_(std::move(name)) { impl_->add(*this); } diff --git a/src/libxrpl/crypto/RFC1751.cpp b/src/libxrpl/crypto/RFC1751.cpp index 30f2c3a5b8..16482945d2 100644 --- a/src/libxrpl/crypto/RFC1751.cpp +++ b/src/libxrpl/crypto/RFC1751.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace xrpl { @@ -306,7 +307,7 @@ RFC1751::standard(std::string& strWord) // Binary search of dictionary. int -RFC1751::wsrch(std::string const& strWord, int iMin, int iMax) +RFC1751::wsrch(std::string_view strWord, int iMin, int iMax) { int iResult = -1; diff --git a/src/libxrpl/ledger/CanonicalTXSet.cpp b/src/libxrpl/ledger/CanonicalTXSet.cpp index a06576342a..df4e88e346 100644 --- a/src/libxrpl/ledger/CanonicalTXSet.cpp +++ b/src/libxrpl/ledger/CanonicalTXSet.cpp @@ -40,14 +40,10 @@ CanonicalTXSet::accountKey(AccountID const& account) } void -CanonicalTXSet::insert(std::shared_ptr const& txn) +CanonicalTXSet::insert(std::shared_ptr txn) { - map_.insert( - std::make_pair( - Key(accountKey(txn->getAccountID(sfAccount)), - txn->getSeqProxy(), - txn->getTransactionID()), - txn)); + Key key(accountKey(txn->getAccountID(sfAccount)), txn->getSeqProxy(), txn->getTransactionID()); + map_.emplace(key, std::move(txn)); } std::shared_ptr diff --git a/src/test/jtx/credentials.h b/src/test/jtx/credentials.h index c2719bf897..4bdd716918 100644 --- a/src/test/jtx/credentials.h +++ b/src/test/jtx/credentials.h @@ -40,7 +40,7 @@ private: std::vector const credentials_; public: - explicit Ids(std::vector const& creds) : credentials_(creds) + explicit Ids(std::vector creds) : credentials_(std::move(creds)) { } diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp index 263fb9451f..1b0107167c 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -133,7 +134,8 @@ public: static void onRequest(Session& session) { - session.write(std::string("Hello, world!\n")); + using namespace std::string_view_literals; + session.write("Hello, world!\n"sv); if (beast::rfc2616::isKeepAlive(session.request())) { session.complete(); diff --git a/src/xrpld/app/consensus/RCLCxLedger.h b/src/xrpld/app/consensus/RCLCxLedger.h index 3d6ed4912b..9f7984aaa6 100644 --- a/src/xrpld/app/consensus/RCLCxLedger.h +++ b/src/xrpld/app/consensus/RCLCxLedger.h @@ -32,7 +32,7 @@ public: @param l The ledger to wrap. */ - RCLCxLedger(std::shared_ptr const& l) : ledger{l} + RCLCxLedger(std::shared_ptr l) : ledger{std::move(l)} { } diff --git a/src/xrpld/app/consensus/RCLValidations.h b/src/xrpld/app/consensus/RCLValidations.h index 16f6e15d4d..37a6f6c743 100644 --- a/src/xrpld/app/consensus/RCLValidations.h +++ b/src/xrpld/app/consensus/RCLValidations.h @@ -32,7 +32,7 @@ public: @param v The validation to wrap. */ - RCLValidation(std::shared_ptr const& v) : val_{v} + RCLValidation(std::shared_ptr v) : val_{std::move(v)} { } diff --git a/src/xrpld/app/ledger/AcceptedLedger.cpp b/src/xrpld/app/ledger/AcceptedLedger.cpp index ea594308bd..6da869198b 100644 --- a/src/xrpld/app/ledger/AcceptedLedger.cpp +++ b/src/xrpld/app/ledger/AcceptedLedger.cpp @@ -5,23 +5,18 @@ #include #include +#include namespace xrpl { -AcceptedLedger::AcceptedLedger(std::shared_ptr const& ledger) : ledger_(ledger) +AcceptedLedger::AcceptedLedger(std::shared_ptr ledger) : ledger_(std::move(ledger)) { transactions_.reserve(256); - - auto insertAll = [&](auto const& txns) { - for (auto const& item : txns) - { - transactions_.emplace_back( - std::make_unique(ledger, item.first, item.second)); - } - }; - - transactions_.reserve(256); - insertAll(ledger->txs); + for (auto const& item : ledger_->txs) + { + transactions_.emplace_back( + std::make_unique(ledger_, item.first, item.second)); + } std::ranges::sort(transactions_, [](auto const& a, auto const& b) { return a->getTxnSeq() < b->getTxnSeq(); diff --git a/src/xrpld/app/ledger/AcceptedLedger.h b/src/xrpld/app/ledger/AcceptedLedger.h index 621bea9e0d..b05af1f18a 100644 --- a/src/xrpld/app/ledger/AcceptedLedger.h +++ b/src/xrpld/app/ledger/AcceptedLedger.h @@ -25,7 +25,7 @@ namespace xrpl { class AcceptedLedger : public CountedObject { public: - AcceptedLedger(std::shared_ptr const& ledger); + AcceptedLedger(std::shared_ptr ledger); [[nodiscard]] std::shared_ptr const& getLedger() const diff --git a/src/xrpld/app/ledger/OpenLedger.h b/src/xrpld/app/ledger/OpenLedger.h index 02e073bc9a..554002d6af 100644 --- a/src/xrpld/app/ledger/OpenLedger.h +++ b/src/xrpld/app/ledger/OpenLedger.h @@ -12,6 +12,7 @@ #include #include +#include namespace xrpl { @@ -149,7 +150,7 @@ public: bool retriesFirst, OrderedTxs& retries, ApplyFlags flags, - std::string const& suffix = "", + std::string_view suffix = "", modify_type const& f = {}); private: diff --git a/src/xrpld/app/ledger/detail/OpenLedger.cpp b/src/xrpld/app/ledger/detail/OpenLedger.cpp index 3bee4b9d13..60599c80d3 100644 --- a/src/xrpld/app/ledger/detail/OpenLedger.cpp +++ b/src/xrpld/app/ledger/detail/OpenLedger.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -82,7 +83,7 @@ OpenLedger::accept( bool retriesFirst, OrderedTxs& retries, ApplyFlags flags, - std::string const& suffix, + std::string_view suffix, modify_type const& f) { JLOG(j_.trace()) << "accept ledger " << ledger->seq() << " " << suffix; diff --git a/src/xrpld/app/ledger/detail/SkipListAcquire.h b/src/xrpld/app/ledger/detail/SkipListAcquire.h index da62578d41..6600b495c9 100644 --- a/src/xrpld/app/ledger/detail/SkipListAcquire.h +++ b/src/xrpld/app/ledger/detail/SkipListAcquire.h @@ -35,8 +35,8 @@ public: std::uint32_t const ledgerSeq; std::vector const skipList; - SkipListData(std::uint32_t const ledgerSeq, std::vector const& skipList) - : ledgerSeq(ledgerSeq), skipList(skipList) + SkipListData(std::uint32_t const ledgerSeq, std::vector skipList) + : ledgerSeq(ledgerSeq), skipList(std::move(skipList)) { } }; diff --git a/src/xrpld/app/misc/detail/ValidatorList.cpp b/src/xrpld/app/misc/detail/ValidatorList.cpp index 0981c32050..a9e7156158 100644 --- a/src/xrpld/app/misc/detail/ValidatorList.cpp +++ b/src/xrpld/app/misc/detail/ValidatorList.cpp @@ -1777,7 +1777,7 @@ ValidatorList::getAvailable( { std::shared_lock const readLock{mutex_}; - auto const keyBlob = strViewUnHex(pubKey); + auto const keyBlob = strUnHex(pubKey); if (!keyBlob || !publicKeyType(makeSlice(*keyBlob))) { diff --git a/src/xrpld/overlay/detail/Cluster.cpp b/src/xrpld/overlay/detail/Cluster.cpp index dcb40a54f5..7855c9647d 100644 --- a/src/xrpld/overlay/detail/Cluster.cpp +++ b/src/xrpld/overlay/detail/Cluster.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace xrpl { @@ -67,7 +68,7 @@ Cluster::update( iter = nodes_.erase(iter); } - nodes_.emplace_hint(iter, identity, name, loadFee, reportTime); + nodes_.emplace_hint(iter, identity, std::move(name), loadFee, reportTime); return true; } diff --git a/src/xrpld/peerfinder/PeerfinderManager.h b/src/xrpld/peerfinder/PeerfinderManager.h index f88b1b637c..ec4beb2db4 100644 --- a/src/xrpld/peerfinder/PeerfinderManager.h +++ b/src/xrpld/peerfinder/PeerfinderManager.h @@ -201,7 +201,7 @@ public: file, along with the set of corresponding IP addresses. */ virtual void - addFixedPeer(std::string const& name, std::vector const& addresses) = 0; + 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. diff --git a/src/xrpld/peerfinder/detail/Logic.h b/src/xrpld/peerfinder/detail/Logic.h index 3ebe0cf2f4..815858cf00 100644 --- a/src/xrpld/peerfinder/detail/Logic.h +++ b/src/xrpld/peerfinder/detail/Logic.h @@ -148,13 +148,13 @@ public: } void - addFixedPeer(std::string const& name, beast::IP::Endpoint const& ep) + addFixedPeer(std::string_view name, beast::IP::Endpoint const& ep) { addFixedPeer(name, std::vector{ep}); } void - addFixedPeer(std::string const& name, std::vector const& addresses) + addFixedPeer(std::string_view name, std::vector const& addresses) { std::scoped_lock const _(lock); diff --git a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp index 873c18aad9..f51f3630eb 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -99,8 +100,7 @@ public: } void - addFixedPeer(std::string const& name, std::vector const& addresses) - override + addFixedPeer(std::string_view name, std::vector const& addresses) override { logic_.addFixedPeer(name, addresses); } diff --git a/src/xrpld/rpc/detail/AssetCache.cpp b/src/xrpld/rpc/detail/AssetCache.cpp index 0976290d4c..23cc31252d 100644 --- a/src/xrpld/rpc/detail/AssetCache.cpp +++ b/src/xrpld/rpc/detail/AssetCache.cpp @@ -22,8 +22,8 @@ namespace xrpl { -AssetCache::AssetCache(std::shared_ptr const& ledger, beast::Journal j) - : ledger_(ledger), journal_(j) +AssetCache::AssetCache(std::shared_ptr ledger, beast::Journal j) + : ledger_(std::move(ledger)), journal_(j) { JLOG(journal_.debug()) << "created for ledger " << ledger_->header().seq; } diff --git a/src/xrpld/rpc/detail/AssetCache.h b/src/xrpld/rpc/detail/AssetCache.h index dd53620cdf..e53bc3ff94 100644 --- a/src/xrpld/rpc/detail/AssetCache.h +++ b/src/xrpld/rpc/detail/AssetCache.h @@ -17,7 +17,7 @@ namespace xrpl { class AssetCache final : public CountedObject { public: - explicit AssetCache(std::shared_ptr const& l, beast::Journal j); + explicit AssetCache(std::shared_ptr l, beast::Journal j); ~AssetCache(); [[nodiscard]] std::shared_ptr const& diff --git a/src/xrpld/rpc/detail/PathRequest.cpp b/src/xrpld/rpc/detail/PathRequest.cpp index 06507319f7..3c09917dad 100644 --- a/src/xrpld/rpc/detail/PathRequest.cpp +++ b/src/xrpld/rpc/detail/PathRequest.cpp @@ -72,7 +72,7 @@ PathRequest::PathRequest( PathRequest::PathRequest( Application& app, - std::function const& completion, + std::function completion, Resource::Consumer& consumer, int id, PathRequestManager& owner, @@ -80,7 +80,7 @@ PathRequest::PathRequest( : app_(app) , journal_(journal) , owner_(owner) - , fCompletion_(completion) + , fCompletion_(std::move(completion)) , consumer_(consumer) , jvStatus_(json::ValueType::Object) , lastIndex_(0) diff --git a/src/xrpld/rpc/detail/PathRequest.h b/src/xrpld/rpc/detail/PathRequest.h index de8c10de0e..372223e99f 100644 --- a/src/xrpld/rpc/detail/PathRequest.h +++ b/src/xrpld/rpc/detail/PathRequest.h @@ -51,7 +51,7 @@ public: // Completion function is called after path update is complete PathRequest( Application& app, - std::function const& completion, + std::function completion, Resource::Consumer& consumer, int id, PathRequestManager&, From c552eb333f5625aaee67698af2e6771ba729d23c Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 9 Jun 2026 10:58:21 -0400 Subject: [PATCH 73/87] refactor: Change config section and key string literals into constants (#7095) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- .../scripts/levelization/results/ordering.txt | 22 ++ cmake/XrplCore.cmake | 14 +- include/xrpl/{basics => config}/BasicConfig.h | 0 include/xrpl/config/Constants.h | 180 ++++++++++ include/xrpl/core/PerfLog.h | 2 +- include/xrpl/nodestore/Database.h | 5 +- include/xrpl/nodestore/Factory.h | 5 +- .../xrpl/nodestore/detail/DatabaseNodeImp.h | 10 +- include/xrpl/server/Port.h | 4 +- .../{basics => config}/BasicConfig.cpp | 2 +- src/libxrpl/nodestore/Database.cpp | 7 +- src/libxrpl/nodestore/DatabaseRotatingImp.cpp | 2 +- src/libxrpl/nodestore/ManagerImp.cpp | 5 +- .../nodestore/backend/MemoryFactory.cpp | 5 +- src/libxrpl/nodestore/backend/NuDBFactory.cpp | 9 +- src/libxrpl/nodestore/backend/NullFactory.cpp | 2 +- .../nodestore/backend/RocksDBFactory.cpp | 49 +-- src/libxrpl/rdb/SociDB.cpp | 9 +- src/libxrpl/server/Port.cpp | 48 +-- src/libxrpl/server/State.cpp | 2 +- src/test/app/AmendmentTable_test.cpp | 8 +- src/test/app/Batch_test.cpp | 20 +- src/test/app/FeeVote_test.cpp | 2 +- src/test/app/GRPCServerTLS_test.cpp | 141 ++++---- src/test/app/HashRouter_test.cpp | 37 +- src/test/app/Manifest_test.cpp | 3 +- src/test/app/MultiSign_test.cpp | 6 +- src/test/app/Regression_test.cpp | 13 +- src/test/app/SHAMapStore_test.cpp | 23 +- src/test/app/TxQ_test.cpp | 152 ++++---- src/test/app/ValidatorKeys_test.cpp | 16 +- src/test/app/ValidatorList_test.cpp | 53 +-- src/test/core/Config_test.cpp | 182 +++++----- src/test/core/SociDB_test.cpp | 7 +- src/test/jtx/Env_test.cpp | 3 +- src/test/jtx/envconfig.h | 5 - src/test/jtx/impl/JSONRPCClient.cpp | 7 +- src/test/jtx/impl/WSClient.cpp | 7 +- src/test/jtx/impl/envconfig.cpp | 119 +++---- src/test/nodestore/Backend_test.cpp | 7 +- src/test/nodestore/Database_test.cpp | 106 +++--- src/test/nodestore/NuDBFactory_test.cpp | 9 +- src/test/nodestore/Timing_test.cpp | 8 +- src/test/overlay/cluster_test.cpp | 2 +- src/test/rpc/AmendmentBlocked_test.cpp | 5 +- src/test/rpc/Feature_test.cpp | 5 +- src/test/rpc/JSONRPC_test.cpp | 7 +- src/test/rpc/LedgerRPC_test.cpp | 7 +- src/test/rpc/ManifestRPC_test.cpp | 4 +- src/test/rpc/RPCOverload_test.cpp | 5 +- src/test/rpc/ServerInfo_test.cpp | 8 +- src/test/rpc/Simulate_test.cpp | 3 +- src/test/rpc/Subscribe_test.cpp | 7 +- src/test/rpc/ValidatorInfo_test.cpp | 4 +- src/test/rpc/ValidatorRPC_test.cpp | 20 +- src/test/server/ServerStatus_test.cpp | 128 +++---- src/test/server/Server_test.cpp | 68 ++-- src/test/shamap/common.h | 6 +- src/tests/libxrpl/helpers/TestFamily.h | 6 +- src/xrpld/app/main/Application.cpp | 41 +-- src/xrpld/app/main/CollectorManager.cpp | 9 +- src/xrpld/app/main/CollectorManager.h | 2 +- src/xrpld/app/main/GRPCServer.cpp | 22 +- src/xrpld/app/main/Main.cpp | 6 +- src/xrpld/app/main/NodeIdentity.cpp | 11 +- src/xrpld/app/misc/NetworkOPs.cpp | 13 +- src/xrpld/app/misc/SHAMapStoreImp.cpp | 57 +-- src/xrpld/app/misc/ValidatorList.h | 4 +- src/xrpld/app/misc/detail/AmendmentTable.cpp | 2 +- src/xrpld/app/misc/detail/TxQ.cpp | 29 +- src/xrpld/app/misc/detail/ValidatorKeys.cpp | 23 +- .../app/misc/detail/setup_HashRouter.cpp | 9 +- src/xrpld/app/rdb/backend/detail/Node.cpp | 3 +- src/xrpld/app/rdb/detail/PeerFinder.cpp | 2 +- src/xrpld/core/Config.h | 2 +- src/xrpld/core/ConfigSections.h | 80 ----- src/xrpld/core/detail/Config.cpp | 329 ++++++++++-------- src/xrpld/overlay/Cluster.h | 2 +- src/xrpld/overlay/detail/Cluster.cpp | 2 +- src/xrpld/overlay/detail/OverlayImpl.cpp | 25 +- .../peerfinder/detail/PeerfinderManager.cpp | 2 +- src/xrpld/perflog/detail/PerfLogImp.cpp | 5 +- src/xrpld/rpc/detail/ServerHandler.cpp | 10 +- 83 files changed, 1262 insertions(+), 1029 deletions(-) rename include/xrpl/{basics => config}/BasicConfig.h (100%) create mode 100644 include/xrpl/config/Constants.h rename src/libxrpl/{basics => config}/BasicConfig.cpp (99%) delete mode 100644 src/xrpld/core/ConfigSections.h diff --git a/.github/scripts/levelization/results/ordering.txt b/.github/scripts/levelization/results/ordering.txt index c2000d1768..12176ec0d4 100644 --- a/.github/scripts/levelization/results/ordering.txt +++ b/.github/scripts/levelization/results/ordering.txt @@ -1,6 +1,8 @@ libxrpl.basics > xrpl.basics libxrpl.conditions > xrpl.basics libxrpl.conditions > xrpl.conditions +libxrpl.config > xrpl.basics +libxrpl.config > xrpl.config libxrpl.core > xrpl.basics libxrpl.core > xrpl.core libxrpl.core > xrpl.json @@ -17,6 +19,7 @@ libxrpl.ledger > xrpl.shamap libxrpl.net > xrpl.basics libxrpl.net > xrpl.net libxrpl.nodestore > xrpl.basics +libxrpl.nodestore > xrpl.config libxrpl.nodestore > xrpl.json libxrpl.nodestore > xrpl.nodestore libxrpl.nodestore > xrpl.protocol @@ -24,6 +27,7 @@ libxrpl.protocol > xrpl.basics libxrpl.protocol > xrpl.json libxrpl.protocol > xrpl.protocol libxrpl.rdb > xrpl.basics +libxrpl.rdb > xrpl.config libxrpl.rdb > xrpl.core libxrpl.rdb > xrpl.rdb libxrpl.resource > xrpl.basics @@ -31,6 +35,7 @@ libxrpl.resource > xrpl.json libxrpl.resource > xrpl.protocol libxrpl.resource > xrpl.resource libxrpl.server > xrpl.basics +libxrpl.server > xrpl.config libxrpl.server > xrpl.core libxrpl.server > xrpl.json libxrpl.server > xrpl.protocol @@ -52,6 +57,7 @@ libxrpl.tx > xrpl.tx test.app > test.jtx test.app > test.unit_test test.app > xrpl.basics +test.app > xrpl.config test.app > xrpl.core test.app > xrpld.app test.app > xrpld.consensus @@ -90,6 +96,7 @@ test.consensus > xrpl.tx test.core > test.jtx test.core > test.unit_test test.core > xrpl.basics +test.core > xrpl.config test.core > xrpl.core test.core > xrpld.core test.core > xrpl.json @@ -104,6 +111,7 @@ test.csf > xrpl.protocol test.json > test.jtx test.json > xrpl.json test.jtx > xrpl.basics +test.jtx > xrpl.config test.jtx > xrpl.core test.jtx > xrpld.app test.jtx > xrpld.core @@ -126,6 +134,7 @@ test.ledger > xrpl.protocol test.nodestore > test.jtx test.nodestore > test.unit_test test.nodestore > xrpl.basics +test.nodestore > xrpl.config test.nodestore > xrpld.core test.nodestore > xrpl.nodestore test.nodestore > xrpl.protocol @@ -133,6 +142,7 @@ test.nodestore > xrpl.rdb test.overlay > test.jtx test.overlay > test.unit_test test.overlay > xrpl.basics +test.overlay > xrpl.config test.overlay > xrpld.app test.overlay > xrpld.core test.overlay > xrpld.overlay @@ -159,6 +169,7 @@ test.resource > xrpl.basics test.resource > xrpl.resource test.rpc > test.jtx test.rpc > xrpl.basics +test.rpc > xrpl.config test.rpc > xrpl.core test.rpc > xrpld.app test.rpc > xrpld.core @@ -173,6 +184,7 @@ test.rpc > xrpl.tx test.server > test.jtx test.server > test.unit_test test.server > xrpl.basics +test.server > xrpl.config test.server > xrpld.app test.server > xrpld.core test.server > xrpl.json @@ -180,6 +192,7 @@ test.server > xrpl.protocol test.server > xrpl.server test.shamap > test.unit_test test.shamap > xrpl.basics +test.shamap > xrpl.config test.shamap > xrpl.nodestore test.shamap > xrpl.protocol test.shamap > xrpl.shamap @@ -188,6 +201,7 @@ test.toplevel > xrpl.json test.unit_test > xrpl.basics test.unit_test > xrpl.protocol tests.libxrpl > xrpl.basics +tests.libxrpl > xrpl.config tests.libxrpl > xrpl.core tests.libxrpl > xrpl.json tests.libxrpl > xrpl.ledger @@ -200,6 +214,7 @@ tests.libxrpl > xrpl.shamap tests.libxrpl > xrpl.tx xrpl.conditions > xrpl.basics xrpl.conditions > xrpl.protocol +xrpl.config > xrpl.basics xrpl.core > xrpl.basics xrpl.core > xrpl.json xrpl.core > xrpl.protocol @@ -210,6 +225,7 @@ xrpl.ledger > xrpl.server xrpl.ledger > xrpl.shamap xrpl.net > xrpl.basics xrpl.nodestore > xrpl.basics +xrpl.nodestore > xrpl.config xrpl.nodestore > xrpl.protocol xrpl.protocol > xrpl.basics xrpl.protocol > xrpl.json @@ -237,6 +253,7 @@ xrpl.tx > xrpl.ledger xrpl.tx > xrpl.protocol xrpld.app > test.unit_test xrpld.app > xrpl.basics +xrpld.app > xrpl.config xrpld.app > xrpl.core xrpld.app > xrpld.consensus xrpld.app > xrpld.core @@ -255,11 +272,13 @@ xrpld.consensus > xrpl.json xrpld.consensus > xrpl.ledger xrpld.consensus > xrpl.protocol xrpld.core > xrpl.basics +xrpld.core > xrpl.config xrpld.core > xrpl.core xrpld.core > xrpl.net xrpld.core > xrpl.protocol xrpld.core > xrpl.rdb xrpld.overlay > xrpl.basics +xrpld.overlay > xrpl.config xrpld.overlay > xrpl.core xrpld.overlay > xrpld.consensus xrpld.overlay > xrpld.core @@ -272,15 +291,18 @@ xrpld.overlay > xrpl.server xrpld.overlay > xrpl.shamap xrpld.overlay > xrpl.tx xrpld.peerfinder > xrpl.basics +xrpld.peerfinder > xrpl.config xrpld.peerfinder > xrpld.core xrpld.peerfinder > xrpl.protocol xrpld.peerfinder > xrpl.rdb xrpld.perflog > xrpl.basics +xrpld.perflog > xrpl.config xrpld.perflog > xrpl.core xrpld.perflog > xrpld.rpc xrpld.perflog > xrpl.json xrpld.perflog > xrpl.protocol xrpld.rpc > xrpl.basics +xrpld.rpc > xrpl.config xrpld.rpc > xrpl.core xrpld.rpc > xrpld.core xrpld.rpc > xrpl.json diff --git a/cmake/XrplCore.cmake b/cmake/XrplCore.cmake index 9b1dc74049..52d7714a99 100644 --- a/cmake/XrplCore.cmake +++ b/cmake/XrplCore.cmake @@ -94,6 +94,9 @@ add_module(xrpl basics) target_link_libraries(xrpl.libxrpl.basics PUBLIC xrpl.libxrpl.beast) # Level 03 +add_module(xrpl config) +target_link_libraries(xrpl.libxrpl.config PUBLIC xrpl.libxrpl.basics) + add_module(xrpl json) target_link_libraries(xrpl.libxrpl.json PUBLIC xrpl.libxrpl.basics) @@ -120,6 +123,7 @@ target_link_libraries( xrpl.libxrpl.core PUBLIC xrpl.libxrpl.basics + xrpl.libxrpl.config xrpl.libxrpl.json xrpl.libxrpl.protocol xrpl.libxrpl.protocol_autogen @@ -143,7 +147,11 @@ target_link_libraries( add_module(xrpl nodestore) target_link_libraries( xrpl.libxrpl.nodestore - PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol + PUBLIC + xrpl.libxrpl.basics + xrpl.libxrpl.config + xrpl.libxrpl.json + xrpl.libxrpl.protocol ) add_module(xrpl shamap) @@ -159,13 +167,14 @@ target_link_libraries( add_module(xrpl rdb) target_link_libraries( xrpl.libxrpl.rdb - PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.core + PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.config xrpl.libxrpl.core ) add_module(xrpl server) target_link_libraries( xrpl.libxrpl.server PUBLIC + xrpl.libxrpl.config xrpl.libxrpl.protocol xrpl.libxrpl.core xrpl.libxrpl.rdb @@ -210,6 +219,7 @@ target_link_modules( basics beast conditions + config core crypto git diff --git a/include/xrpl/basics/BasicConfig.h b/include/xrpl/config/BasicConfig.h similarity index 100% rename from include/xrpl/basics/BasicConfig.h rename to include/xrpl/config/BasicConfig.h diff --git a/include/xrpl/config/Constants.h b/include/xrpl/config/Constants.h new file mode 100644 index 0000000000..5514e0e77b --- /dev/null +++ b/include/xrpl/config/Constants.h @@ -0,0 +1,180 @@ +#pragma once + +namespace xrpl { + +struct Sections +{ + static constexpr auto kAmendments = "amendments"; + static constexpr auto kAmendmentMajorityTime = "amendment_majority_time"; + static constexpr auto kBetaRpcApi = "beta_rpc_api"; + static constexpr auto kClusterNodes = "cluster_nodes"; + static constexpr auto kCompression = "compression"; + static constexpr auto kCrawl = "crawl"; + static constexpr auto kDatabasePath = "database_path"; + static constexpr auto kDebugLogfile = "debug_logfile"; + static constexpr auto kElbSupport = "elb_support"; + static constexpr auto kFeatures = "features"; + static constexpr auto kFeeDefault = "fee_default"; + static constexpr auto kFetchDepth = "fetch_depth"; + static constexpr auto kHashrouter = "hashrouter"; + static constexpr auto kImportNodeDatabase = "import_db"; + static constexpr auto kInsight = "insight"; + static constexpr auto kIoWorkers = "io_workers"; + static constexpr auto kIps = "ips"; + static constexpr auto kIpsFixed = "ips_fixed"; + static constexpr auto kLedgerHistory = "ledger_history"; + static constexpr auto kLedgerReplay = "ledger_replay"; + static constexpr auto kLedgerTxTables = "ledger_tx_tables"; + static constexpr auto kMaxTransactions = "max_transactions"; + static constexpr auto kNetworkId = "network_id"; + static constexpr auto kNetworkQuorum = "network_quorum"; + static constexpr auto kNodeDatabase = "node_db"; + static constexpr auto kNodeSeed = "node_seed"; + static constexpr auto kNodeSize = "node_size"; + static constexpr auto kOverlay = "overlay"; + static constexpr auto kPathSearch = "path_search"; + static constexpr auto kPathSearchFast = "path_search_fast"; + static constexpr auto kPathSearchMax = "path_search_max"; + static constexpr auto kPathSearchOld = "path_search_old"; + static constexpr auto kPeerPrivate = "peer_private"; + static constexpr auto kPeersInMax = "peers_in_max"; + static constexpr auto kPeersMax = "peers_max"; + static constexpr auto kPeersOutMax = "peers_out_max"; + static constexpr auto kPerf = "perf"; + static constexpr auto kPortGrpc = "port_grpc"; + static constexpr auto kPortPeer = "port_peer"; + static constexpr auto kPortRpc = "port_rpc"; + static constexpr auto kPortWs = "port_ws"; + static constexpr auto kPortWssAdmin = "port_wss_admin"; + static constexpr auto kPrefetchWorkers = "prefetch_workers"; + static constexpr auto kReduceRelay = "reduce_relay"; + static constexpr auto kRelationalDb = "relational_db"; + static constexpr auto kRelayProposals = "relay_proposals"; + static constexpr auto kRelayValidations = "relay_validations"; + static constexpr auto kRpcStartup = "rpc_startup"; + static constexpr auto kServer = "server"; + static constexpr auto kServerDomain = "server_domain"; + static constexpr auto kSigningSupport = "signing_support"; + static constexpr auto kSntp = "sntp_servers"; + static constexpr auto kSqdb = "sqdb"; + static constexpr auto kSqlite = "sqlite"; + static constexpr auto kSslVerify = "ssl_verify"; + static constexpr auto kSslVerifyDir = "ssl_verify_dir"; + static constexpr auto kSslVerifyFile = "ssl_verify_file"; + static constexpr auto kSweepInterval = "sweep_interval"; + static constexpr auto kTransactionQueue = "transaction_queue"; + static constexpr auto kValidationSeed = "validation_seed"; + static constexpr auto kValidatorKeys = "validator_keys"; + static constexpr auto kValidatorKeyRevocation = "validator_key_revocation"; + static constexpr auto kValidatorListKeys = "validator_list_keys"; + static constexpr auto kValidatorListSites = "validator_list_sites"; + static constexpr auto kValidatorListThreshold = "validator_list_threshold"; + static constexpr auto kValidatorToken = "validator_token"; + static constexpr auto kValidators = "validators"; + static constexpr auto kValidatorsFile = "validators_file"; + static constexpr auto kVetoAmendments = "veto_amendments"; + static constexpr auto kVl = "vl"; + static constexpr auto kVoting = "voting"; + static constexpr auto kWorkers = "workers"; +}; + +struct Keys +{ + static constexpr auto kAccountReserve = "account_reserve"; + static constexpr auto kAddress = "address"; + static constexpr auto kAdmin = "admin"; + static constexpr auto kAdminPassword = "admin_password"; + static constexpr auto kAdminUser = "admin_user"; + static constexpr auto kAdvisoryDelete = "advisory_delete"; + static constexpr auto kAgeThresholdSeconds = "age_threshold_seconds"; + static constexpr auto kBackOff = "backOff"; + static constexpr auto kBackOffMilliseconds = "back_off_milliseconds"; + static constexpr auto kBackend = "backend"; + static constexpr auto kBbtOptions = "bbt_options"; + static constexpr auto kBgThreads = "bg_threads"; + static constexpr auto kBlockSize = "block_size"; + static constexpr auto kCacheAge = "cache_age"; + static constexpr auto kCacheMb = "cache_mb"; + static constexpr auto kCacheSize = "cache_size"; + static constexpr auto kClientMaxWindowBits = "client_max_window_bits"; + static constexpr auto kClientNoContextTakeover = "client_no_context_takeover"; + static constexpr auto kCompressLevel = "compress_level"; + static constexpr auto kCounts = "counts"; + static constexpr auto kDeleteBatch = "delete_batch"; + static constexpr auto kEarliestSeq = "earliest_seq"; + static constexpr auto kFastLoad = "fast_load"; + static constexpr auto kFileSizeMb = "file_size_mb"; + static constexpr auto kFileSizeMult = "file_size_mult"; + static constexpr auto kFilterBits = "filter_bits"; + static constexpr auto kFilterFull = "filter_full"; + static constexpr auto kHardSet = "hard_set"; + static constexpr auto kHighThreads = "high_threads"; + static constexpr auto kHoldTime = "hold_time"; + static constexpr auto kIp = "ip"; + static constexpr auto kJournalMode = "journal_mode"; + static constexpr auto kJournalSizeLimit = "journal_size_limit"; + static constexpr auto kLedgersInQueue = "ledgers_in_queue"; + static constexpr auto kLimit = "limit"; + static constexpr auto kLogInterval = "log_interval"; + static constexpr auto kMaxDivergedTime = "max_diverged_time"; + static constexpr auto kMaxLedgerCountsToStore = "max_ledger_counts_to_store"; + static constexpr auto kMaxUnknownTime = "max_unknown_time"; + static constexpr auto kMaximumTxnInLedger = "maximum_txn_in_ledger"; + static constexpr auto kMaximumTxnPerAccount = "maximum_txn_per_account"; + static constexpr auto kMemoryLevel = "memory_level"; + static constexpr auto kMinLedgersToComputeSizeLimit = "min_ledgers_to_compute_size_limit"; + static constexpr auto kMinimumEscalationMultiplier = "minimum_escalation_multiplier"; + static constexpr auto kMinimumLastLedgerBuffer = "minimum_last_ledger_buffer"; + static constexpr auto kMinimumQueueSize = "minimum_queue_size"; + static constexpr auto kMinimumTxnInLedger = "minimum_txn_in_ledger"; + static constexpr auto kMinimumTxnInLedgerStandalone = "minimum_txn_in_ledger_standalone"; + static constexpr auto kNormalConsensusIncreasePercent = "normal_consensus_increase_percent"; + static constexpr auto kNudbBlockSize = "nudb_block_size"; + static constexpr auto kOnlineDelete = "online_delete"; + static constexpr auto kOpenFiles = "open_files"; + static constexpr auto kOptions = "options"; + static constexpr auto kOverlay = "overlay"; + static constexpr auto kOwnerReserve = "owner_reserve"; + static constexpr auto kPageSize = "page_size"; + static constexpr auto kPassword = "password"; + static constexpr auto kPath = "path"; + static constexpr auto kPermessageDeflate = "permessage_deflate"; + static constexpr auto kPort = "port"; + static constexpr auto kPrefix = "prefix"; + static constexpr auto kProtocol = "protocol"; + static constexpr auto kRecoveryWaitSeconds = "recovery_wait_seconds"; + static constexpr auto kReferenceFee = "reference_fee"; + static constexpr auto kRelayTime = "relay_time"; + static constexpr auto kRetrySequencePercent = "retry_sequence_percent"; + static constexpr auto kRqBundle = "rq_bundle"; + static constexpr auto kSafetyLevel = "safety_level"; + static constexpr auto kSecureGateway = "secure_gateway"; + static constexpr auto kSendQueueLimit = "send_queue_limit"; + static constexpr auto kServer = "server"; + static constexpr auto kServerMaxWindowBits = "server_max_window_bits"; + static constexpr auto kServerNoContextTakeover = "server_no_context_takeover"; + static constexpr auto kSlowConsensusDecreasePercent = "slow_consensus_decrease_percent"; + static constexpr auto kSslCert = "ssl_cert"; + static constexpr auto kSslCertChain = "ssl_cert_chain"; + static constexpr auto kSslChain = "ssl_chain"; + static constexpr auto kSslCiphers = "ssl_ciphers"; + static constexpr auto kSslClientCa = "ssl_client_ca"; + static constexpr auto kSslKey = "ssl_key"; + static constexpr auto kSynchronous = "synchronous"; + static constexpr auto kTargetTxnInLedger = "target_txn_in_ledger"; + static constexpr auto kTempStore = "temp_store"; + static constexpr auto kTxEnable = "tx_enable"; + static constexpr auto kTxMetrics = "tx_metrics"; + static constexpr auto kTxMinPeers = "tx_min_peers"; + static constexpr auto kTxRelayPercentage = "tx_relay_percentage"; + static constexpr auto kType = "type"; + static constexpr auto kUniversalCompaction = "universal_compaction"; + static constexpr auto kUnl = "unl"; + static constexpr auto kUseTxTables = "use_tx_tables"; + static constexpr auto kUser = "user"; + static constexpr auto kVpBaseSquelchEnable = "vp_base_squelch_enable"; + static constexpr auto kVpBaseSquelchMaxSelectedPeers = "vp_base_squelch_max_selected_peers"; + static constexpr auto kVpEnable = "vp_enable"; +}; + +} // namespace xrpl diff --git a/include/xrpl/core/PerfLog.h b/include/xrpl/core/PerfLog.h index 38318c745d..ca0d9333a4 100644 --- a/include/xrpl/core/PerfLog.h +++ b/include/xrpl/core/PerfLog.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -18,6 +17,7 @@ class Journal; namespace xrpl { class Application; +class Section; namespace perf { /** diff --git a/include/xrpl/nodestore/Database.h b/include/xrpl/nodestore/Database.h index 438a3cc7fc..68c5dcefb6 100644 --- a/include/xrpl/nodestore/Database.h +++ b/include/xrpl/nodestore/Database.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -10,6 +9,10 @@ #include +namespace xrpl { +class Section; +} // namespace xrpl + namespace xrpl::NodeStore { /** Persistency layer for NodeObject diff --git a/include/xrpl/nodestore/Factory.h b/include/xrpl/nodestore/Factory.h index c40be62d21..3e6ba76a08 100644 --- a/include/xrpl/nodestore/Factory.h +++ b/include/xrpl/nodestore/Factory.h @@ -1,12 +1,15 @@ #pragma once -#include #include #include #include #include +namespace xrpl { +class Section; +} // namespace xrpl + namespace xrpl::NodeStore { /** Base class for backend factories. */ diff --git a/include/xrpl/nodestore/detail/DatabaseNodeImp.h b/include/xrpl/nodestore/detail/DatabaseNodeImp.h index 951c60c8c7..38b8763f31 100644 --- a/include/xrpl/nodestore/detail/DatabaseNodeImp.h +++ b/include/xrpl/nodestore/detail/DatabaseNodeImp.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include namespace xrpl::NodeStore { @@ -24,16 +26,16 @@ public: { std::optional cacheSize, cacheAge; - if (config.exists("cache_size")) + if (config.exists(Keys::kCacheSize)) { - cacheSize = get(config, "cache_size"); + cacheSize = get(config, Keys::kCacheSize); if (cacheSize.value() < 0) Throw("Specified negative value for cache_size"); } - if (config.exists("cache_age")) + if (config.exists(Keys::kCacheAge)) { - cacheAge = get(config, "cache_age"); + cacheAge = get(config, Keys::kCacheAge); if (cacheAge.value() < 0) Throw("Specified negative value for cache_age"); } diff --git a/include/xrpl/server/Port.h b/include/xrpl/server/Port.h index ac9b855cf0..fd773c78fc 100644 --- a/include/xrpl/server/Port.h +++ b/include/xrpl/server/Port.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -14,6 +13,7 @@ #include #include #include +#include namespace boost::asio::ssl { class context; // NOLINT(readability-identifier-naming) -- external library name @@ -21,6 +21,8 @@ class context; // NOLINT(readability-identifier-naming) -- external library nam namespace xrpl { +class Section; + /** Configuration information for a Server listening port. */ struct Port { diff --git a/src/libxrpl/basics/BasicConfig.cpp b/src/libxrpl/config/BasicConfig.cpp similarity index 99% rename from src/libxrpl/basics/BasicConfig.cpp rename to src/libxrpl/config/BasicConfig.cpp index 9fe79bdf4e..6b94473392 100644 --- a/src/libxrpl/basics/BasicConfig.cpp +++ b/src/libxrpl/config/BasicConfig.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/src/libxrpl/nodestore/Database.cpp b/src/libxrpl/nodestore/Database.cpp index b584aca268..ac51dbfb2c 100644 --- a/src/libxrpl/nodestore/Database.cpp +++ b/src/libxrpl/nodestore/Database.cpp @@ -1,12 +1,13 @@ #include -#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -38,8 +39,8 @@ Database::Database( beast::Journal journal) : j_(journal) , scheduler_(scheduler) - , earliestLedgerSeq_(get(config, "earliest_seq", kXrpLedgerEarliestSeq)) - , requestBundle_(get(config, "rq_bundle", 4)) + , earliestLedgerSeq_(get(config, Keys::kEarliestSeq, kXrpLedgerEarliestSeq)) + , requestBundle_(get(config, Keys::kRqBundle, 4)) , readThreads_(std::max(1, readThreads)) { XRPL_ASSERT(readThreads, "xrpl::NodeStore::Database::Database : nonzero threads input"); diff --git a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp index 1de6a83b4c..7f4dca3ed1 100644 --- a/src/libxrpl/nodestore/DatabaseRotatingImp.cpp +++ b/src/libxrpl/nodestore/DatabaseRotatingImp.cpp @@ -1,11 +1,11 @@ #include -#include #include #include #include #include #include +#include #include #include #include diff --git a/src/libxrpl/nodestore/ManagerImp.cpp b/src/libxrpl/nodestore/ManagerImp.cpp index 5cefbfd357..5f366079f1 100644 --- a/src/libxrpl/nodestore/ManagerImp.cpp +++ b/src/libxrpl/nodestore/ManagerImp.cpp @@ -1,9 +1,10 @@ #include -#include #include #include #include +#include +#include #include #include #include @@ -66,7 +67,7 @@ ManagerImp::makeBackend( Scheduler& scheduler, beast::Journal journal) { - std::string const type{get(parameters, "type")}; + std::string const type{get(parameters, Keys::kType)}; if (type.empty()) missingBackend(); diff --git a/src/libxrpl/nodestore/backend/MemoryFactory.cpp b/src/libxrpl/nodestore/backend/MemoryFactory.cpp index 5bdf8e65b5..70578c8613 100644 --- a/src/libxrpl/nodestore/backend/MemoryFactory.cpp +++ b/src/libxrpl/nodestore/backend/MemoryFactory.cpp @@ -1,8 +1,9 @@ -#include #include #include #include #include +#include +#include #include #include #include @@ -90,7 +91,7 @@ private: public: MemoryBackend(size_t keyBytes, Section const& keyValues, beast::Journal journal) - : name_(get(keyValues, "path")), journal_(journal) + : name_(get(keyValues, Keys::kPath)), journal_(journal) { boost::ignore_unused(journal_); // Keep unused journal_ just in case. if (name_.empty()) diff --git a/src/libxrpl/nodestore/backend/NuDBFactory.cpp b/src/libxrpl/nodestore/backend/NuDBFactory.cpp index abf09e871d..749d4020b5 100644 --- a/src/libxrpl/nodestore/backend/NuDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/NuDBFactory.cpp @@ -1,10 +1,11 @@ -#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -72,7 +73,7 @@ public: : j(journal) , keyBytes(keyBytes) , burstSize(burstSize) - , name(get(keyValues, "path")) + , name(get(keyValues, Keys::kPath)) , blockSize(parseBlockSize(name, keyValues, journal)) , deletePath(false) , scheduler(scheduler) @@ -91,7 +92,7 @@ public: : j(journal) , keyBytes(keyBytes) , burstSize(burstSize) - , name(get(keyValues, "path")) + , name(get(keyValues, Keys::kPath)) , blockSize(parseBlockSize(name, keyValues, journal)) , db(context) , deletePath(false) @@ -359,7 +360,7 @@ private: std::size_t const blockSize = defaultSize; std::string blockSizeStr; - if (!getIfExists(keyValues, "nudb_block_size", blockSizeStr)) + if (!getIfExists(keyValues, Keys::kNudbBlockSize, blockSizeStr)) { return blockSize; // Early return with default } diff --git a/src/libxrpl/nodestore/backend/NullFactory.cpp b/src/libxrpl/nodestore/backend/NullFactory.cpp index 36b8139984..e36b13a2e1 100644 --- a/src/libxrpl/nodestore/backend/NullFactory.cpp +++ b/src/libxrpl/nodestore/backend/NullFactory.cpp @@ -1,6 +1,6 @@ -#include #include #include +#include #include #include #include diff --git a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp index d2c193888c..252ff32ccf 100644 --- a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp @@ -1,8 +1,9 @@ -#include #include #include #include #include +#include +#include #include #include #include @@ -111,17 +112,18 @@ public: RocksDBEnv* env) : deletePath_(false), journal(journal), keyBytes(keyBytes), batch(*this, scheduler) { - if (!getIfExists(keyValues, "path", name)) + if (!getIfExists(keyValues, Keys::kPath, name)) Throw("Missing path in RocksDBFactory backend"); rocksdb::BlockBasedTableOptions tableOptions; options.env = env; - bool const hardSet = keyValues.exists("hard_set") && get(keyValues, "hard_set"); + bool const hardSet = + keyValues.exists(Keys::kHardSet) && get(keyValues, Keys::kHardSet); - if (keyValues.exists("cache_mb")) + if (keyValues.exists(Keys::kCacheMb)) { - auto size = get(keyValues, "cache_mb"); + auto size = get(keyValues, Keys::kCacheMb); if (!hardSet && size == 256) size = 1024; @@ -129,14 +131,14 @@ public: tableOptions.block_cache = rocksdb::NewLRUCache(megabytes(size)); } - if (auto const v = get(keyValues, "filter_bits")) + if (auto const v = get(keyValues, Keys::kFilterBits)) { - bool const filterBlocks = - !keyValues.exists("filter_full") || (get(keyValues, "filter_full") == 0); + bool const filterBlocks = !keyValues.exists(Keys::kFilterFull) || + (get(keyValues, Keys::kFilterFull) == 0); tableOptions.filter_policy.reset(rocksdb::NewBloomFilterPolicy(v, filterBlocks)); } - if (getIfExists(keyValues, "open_files", options.max_open_files)) + if (getIfExists(keyValues, Keys::kOpenFiles, options.max_open_files)) { if (!hardSet && options.max_open_files == 2000) options.max_open_files = 8000; @@ -144,9 +146,9 @@ public: fdMinRequired = options.max_open_files + 128; } - if (keyValues.exists("file_size_mb")) + if (keyValues.exists(Keys::kFileSizeMb)) { - auto fileSizeMb = get(keyValues, "file_size_mb"); + auto fileSizeMb = get(keyValues, Keys::kFileSizeMb); if (!hardSet && fileSizeMb == 8) fileSizeMb = 256; @@ -156,16 +158,17 @@ public: options.write_buffer_size = 2 * options.target_file_size_base; } - getIfExists(keyValues, "file_size_mult", options.target_file_size_multiplier); + getIfExists(keyValues, Keys::kFileSizeMult, options.target_file_size_multiplier); - if (keyValues.exists("bg_threads")) + if (keyValues.exists(Keys::kBgThreads)) { - options.env->SetBackgroundThreads(get(keyValues, "bg_threads"), rocksdb::Env::LOW); + options.env->SetBackgroundThreads( + get(keyValues, Keys::kBgThreads), rocksdb::Env::LOW); } - if (keyValues.exists("high_threads")) + if (keyValues.exists(Keys::kHighThreads)) { - auto const highThreads = get(keyValues, "high_threads"); + auto const highThreads = get(keyValues, Keys::kHighThreads); options.env->SetBackgroundThreads(highThreads, rocksdb::Env::HIGH); // If we have high-priority threads, presumably we want to @@ -176,10 +179,10 @@ public: options.compression = rocksdb::kSnappyCompression; - getIfExists(keyValues, "block_size", tableOptions.block_size); + getIfExists(keyValues, Keys::kBlockSize, tableOptions.block_size); - if (keyValues.exists("universal_compaction") && - (get(keyValues, "universal_compaction") != 0)) + if (keyValues.exists(Keys::kUniversalCompaction) && + (get(keyValues, Keys::kUniversalCompaction) != 0)) { options.compaction_style = rocksdb::kCompactionStyleUniversal; options.min_write_buffer_number_to_merge = 2; @@ -187,11 +190,11 @@ public: options.write_buffer_size = 6 * options.target_file_size_base; } - if (keyValues.exists("bbt_options")) + if (keyValues.exists(Keys::kBbtOptions)) { rocksdb::ConfigOptions const configOptions; auto const s = rocksdb::GetBlockBasedTableOptionsFromString( - configOptions, tableOptions, get(keyValues, "bbt_options"), &tableOptions); + configOptions, tableOptions, get(keyValues, Keys::kBbtOptions), &tableOptions); if (!s.ok()) { Throw( @@ -201,10 +204,10 @@ public: options.table_factory.reset(NewBlockBasedTableFactory(tableOptions)); - if (keyValues.exists("options")) + if (keyValues.exists(Keys::kOptions)) { auto const s = - rocksdb::GetOptionsFromString(options, get(keyValues, "options"), &options); + rocksdb::GetOptionsFromString(options, get(keyValues, Keys::kOptions), &options); if (!s.ok()) { Throw( diff --git a/src/libxrpl/rdb/SociDB.cpp b/src/libxrpl/rdb/SociDB.cpp index 541933b3b0..06e58d373f 100644 --- a/src/libxrpl/rdb/SociDB.cpp +++ b/src/libxrpl/rdb/SociDB.cpp @@ -1,5 +1,6 @@ -#include #include +#include +#include #include #include #include @@ -53,13 +54,13 @@ getSociSqliteInit(std::string const& name, std::string const& dir, std::string c std::string getSociInit(BasicConfig const& config, std::string const& dbName) { - auto const& section = config.section("sqdb"); - auto const backendName = get(section, "backend", "sqlite"); + auto const& section = config.section(Sections::kSqdb); + auto const backendName = get(section, Keys::kBackend, "sqlite"); if (backendName != "sqlite") Throw("Unsupported soci backend: " + backendName); - auto const path = config.legacy("database_path"); + auto const path = config.legacy(Sections::kDatabasePath); auto const ext = dbName == "validators" || dbName == "peerfinder" ? ".sqlite" : ".db"; return detail::getSociSqliteInit(dbName, path, ext); } diff --git a/src/libxrpl/server/Port.cpp b/src/libxrpl/server/Port.cpp index b3fd7a1526..c1a79019af 100644 --- a/src/libxrpl/server/Port.cpp +++ b/src/libxrpl/server/Port.cpp @@ -1,11 +1,12 @@ #include -#include #include #include #include #include #include +#include +#include #include #include @@ -195,7 +196,7 @@ parsePort(ParsedPort& port, Section const& section, std::ostream& log) { port.name = section.name(); { - auto const optResult = section.get("ip"); + auto const optResult = section.get(Keys::kIp); if (optResult) { try @@ -212,7 +213,7 @@ parsePort(ParsedPort& port, Section const& section, std::ostream& log) } { - auto const optResult = section.get("port"); + auto const optResult = section.get(Keys::kPort); if (optResult) { try @@ -233,7 +234,7 @@ parsePort(ParsedPort& port, Section const& section, std::ostream& log) } { - auto const optResult = section.get("protocol"); + auto const optResult = section.get(Keys::kProtocol); if (optResult) { for (auto const& s : beast::rfc2616::splitCommas(optResult->begin(), optResult->end())) @@ -242,7 +243,7 @@ parsePort(ParsedPort& port, Section const& section, std::ostream& log) } { - auto const lim = get(section, "limit", "unlimited"); + auto const lim = get(section, Keys::kLimit, "unlimited"); if (!boost::iequals(lim, "unlimited")) { @@ -260,7 +261,7 @@ parsePort(ParsedPort& port, Section const& section, std::ostream& log) } { - auto const optResult = section.get("send_queue_limit"); + auto const optResult = section.get(Keys::kSendQueueLimit); if (optResult) { try @@ -285,27 +286,28 @@ parsePort(ParsedPort& port, Section const& section, std::ostream& log) } } - populate(section, "admin", log, port.adminNetsV4, port.adminNetsV6); - populate(section, "secure_gateway", log, port.secureGatewayNetsV4, port.secureGatewayNetsV6); + populate(section, Keys::kAdmin, log, port.adminNetsV4, port.adminNetsV6); + populate( + section, Keys::kSecureGateway, log, port.secureGatewayNetsV4, port.secureGatewayNetsV6); - set(port.user, "user", section); - set(port.password, "password", section); - set(port.adminUser, "admin_user", section); - set(port.adminPassword, "admin_password", section); - set(port.sslKey, "ssl_key", section); - set(port.sslCert, "ssl_cert", section); - set(port.sslChain, "ssl_chain", section); - set(port.sslCiphers, "ssl_ciphers", section); + set(port.user, Keys::kUser, section); + set(port.password, Keys::kPassword, section); + set(port.adminUser, Keys::kAdminUser, section); + set(port.adminPassword, Keys::kAdminPassword, section); + set(port.sslKey, Keys::kSslKey, section); + set(port.sslCert, Keys::kSslCert, section); + set(port.sslChain, Keys::kSslChain, section); + set(port.sslCiphers, Keys::kSslCiphers, section); - port.pmdOptions.server_enable = section.valueOr("permessage_deflate", true); - port.pmdOptions.client_max_window_bits = section.valueOr("client_max_window_bits", 15); - port.pmdOptions.server_max_window_bits = section.valueOr("server_max_window_bits", 15); + port.pmdOptions.server_enable = section.valueOr(Keys::kPermessageDeflate, true); + port.pmdOptions.client_max_window_bits = section.valueOr(Keys::kClientMaxWindowBits, 15); + port.pmdOptions.server_max_window_bits = section.valueOr(Keys::kServerMaxWindowBits, 15); port.pmdOptions.client_no_context_takeover = - section.valueOr("client_no_context_takeover", false); + section.valueOr(Keys::kClientNoContextTakeover, false); port.pmdOptions.server_no_context_takeover = - section.valueOr("server_no_context_takeover", false); - port.pmdOptions.compLevel = section.valueOr("compress_level", 8); - port.pmdOptions.memLevel = section.valueOr("memory_level", 4); + section.valueOr(Keys::kServerNoContextTakeover, false); + port.pmdOptions.compLevel = section.valueOr(Keys::kCompressLevel, 8); + port.pmdOptions.memLevel = section.valueOr(Keys::kMemoryLevel, 4); } } // namespace xrpl diff --git a/src/libxrpl/server/State.cpp b/src/libxrpl/server/State.cpp index b9cb7c6ff2..d9793f53d0 100644 --- a/src/libxrpl/server/State.cpp +++ b/src/libxrpl/server/State.cpp @@ -1,7 +1,7 @@ #include -#include #include +#include #include #include diff --git a/src/test/app/AmendmentTable_test.cpp b/src/test/app/AmendmentTable_test.cpp index 219aaabdda..7c2087bd4a 100644 --- a/src/test/app/AmendmentTable_test.cpp +++ b/src/test/app/AmendmentTable_test.cpp @@ -4,14 +4,14 @@ #include #include -#include -#include #include #include #include #include #include +#include +#include #include #include #include @@ -83,8 +83,8 @@ private: makeConfig() { auto cfg = test::jtx::envconfig(); - cfg->section(SECTION_AMENDMENTS) = makeSection(SECTION_AMENDMENTS, enabled_); - cfg->section(SECTION_VETO_AMENDMENTS) = makeSection(SECTION_VETO_AMENDMENTS, vetoed_); + cfg->section(Sections::kAmendments) = makeSection(Sections::kAmendments, enabled_); + cfg->section(Sections::kVetoAmendments) = makeSection(Sections::kVetoAmendments, vetoed_); return cfg; } diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index 791bb5a4d6..47f9a84fb9 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -168,13 +170,13 @@ class Batch_test : public beast::unit_test::Suite std::map extraVoting = {}) { auto p = test::jtx::envconfig(); - auto& section = p->section("transaction_queue"); - section.set("ledgers_in_queue", "2"); - section.set("minimum_queue_size", "2"); - section.set("min_ledgers_to_compute_size_limit", "3"); - section.set("max_ledger_counts_to_store", "100"); - section.set("retry_sequence_percent", "25"); - section.set("normal_consensus_increase_percent", "0"); + auto& section = p->section(Sections::kTransactionQueue); + section.set(Keys::kLedgersInQueue, "2"); + section.set(Keys::kMinimumQueueSize, "2"); + section.set(Keys::kMinLedgersToComputeSizeLimit, "3"); + section.set(Keys::kMaxLedgerCountsToStore, "100"); + section.set(Keys::kRetrySequencePercent, "25"); + section.set(Keys::kNormalConsensusIncreasePercent, "0"); for (auto const& [k, v] : extraTxQ) section.set(k, v); @@ -4361,7 +4363,7 @@ class Batch_test : public beast::unit_test::Suite { test::jtx::Env env{ *this, - makeSmallQueueConfig({{"minimum_txn_in_ledger_standalone", "2"}}), + makeSmallQueueConfig({{Keys::kMinimumTxnInLedgerStandalone, "2"}}), features, nullptr, beast::Severity::Error}; @@ -4417,7 +4419,7 @@ class Batch_test : public beast::unit_test::Suite { test::jtx::Env env{ *this, - makeSmallQueueConfig({{"minimum_txn_in_ledger_standalone", "2"}}), + makeSmallQueueConfig({{Keys::kMinimumTxnInLedgerStandalone, "2"}}), features, nullptr, beast::Severity::Error}; diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index 22e8322bb5..bf42e762c6 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -4,9 +4,9 @@ #include #include -#include #include #include +#include #include #include #include diff --git a/src/test/app/GRPCServerTLS_test.cpp b/src/test/app/GRPCServerTLS_test.cpp index c7156fb3a2..ae0d839a6e 100644 --- a/src/test/app/GRPCServerTLS_test.cpp +++ b/src/test/app/GRPCServerTLS_test.cpp @@ -1,9 +1,8 @@ #include #include -#include - #include +#include #include #include @@ -368,7 +367,8 @@ public: Env env(*this, std::move(cfg)); // Verify the server actually started by checking the port - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); // NOLINTBEGIN(bugprone-unchecked-optional-access) grpcPort.has_value() checked above BEAST_EXPECT(*grpcPort > 0); @@ -394,7 +394,8 @@ public: Env env(*this, std::move(cfg)); // Verify the server actually started by checking the port - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); // NOLINTBEGIN(bugprone-unchecked-optional-access) grpcPort.has_value() checked above BEAST_EXPECT(*grpcPort > 0); @@ -431,7 +432,8 @@ public: Env env(*this, std::move(cfg)); // Verify the server actually started by checking the port - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); // NOLINTBEGIN(bugprone-unchecked-optional-access) grpcPort.has_value() checked above BEAST_EXPECT(*grpcPort > 0); @@ -465,9 +467,9 @@ public: // Create config with only cert (missing key) auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", getServerCertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, getServerCertPath().string()); // Intentionally omit ssl_key try @@ -491,9 +493,9 @@ public: // Create config with only key (missing cert) auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", getServerKeyPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, getServerKeyPath().string()); // Intentionally omit ssl_cert try @@ -518,9 +520,9 @@ public: // Test 1: ssl_client_ca specified without any TLS config { auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_client_ca", getCACertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslClientCa, getCACertPath().string()); // Intentionally omit both ssl_cert and ssl_key try @@ -539,10 +541,10 @@ public: // Test 2: ssl_client_ca with only ssl_cert (missing ssl_key) { auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", getServerCertPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_client_ca", getCACertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, getServerCertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslClientCa, getCACertPath().string()); // Intentionally omit ssl_key try @@ -563,10 +565,10 @@ public: // Test 3: ssl_client_ca with only ssl_key (missing ssl_cert) { auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", getServerKeyPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_client_ca", getCACertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, getServerKeyPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslClientCa, getCACertPath().string()); // Intentionally omit ssl_cert try @@ -595,9 +597,9 @@ public: // Test 1: ssl_cert_chain specified without any TLS config { auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert_chain", getCACertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCertChain, getCACertPath().string()); // Intentionally omit both ssl_cert and ssl_key try @@ -616,10 +618,10 @@ public: // Test 2: ssl_cert_chain with only ssl_cert (missing ssl_key) { auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", getServerCertPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert_chain", getCACertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, getServerCertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCertChain, getCACertPath().string()); // Intentionally omit ssl_key try @@ -655,7 +657,8 @@ public: Env env(*this, std::move(cfg)); // Verify the server actually started by checking the port - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); // NOLINTBEGIN(bugprone-unchecked-optional-access) grpcPort.has_value() checked above BEAST_EXPECT(*grpcPort > 0); @@ -684,15 +687,16 @@ public: using namespace jtx; auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", "/nonexistent/path/to/cert.pem"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", getServerKeyPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, "/nonexistent/path/to/cert.pem"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, getServerKeyPath().string()); Env env(*this, std::move(cfg)); // Server should fail to start - verify port is 0 - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); BEAST_EXPECT(*grpcPort == 0); // NOLINT(bugprone-unchecked-optional-access) } @@ -705,15 +709,16 @@ public: using namespace jtx; auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", getServerCertPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", "/nonexistent/path/to/key.pem"); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, getServerCertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, "/nonexistent/path/to/key.pem"); Env env(*this, std::move(cfg)); // Server should fail to start - verify port is 0 - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); BEAST_EXPECT(*grpcPort == 0); // NOLINT(bugprone-unchecked-optional-access) } @@ -726,16 +731,17 @@ public: using namespace jtx; auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", getServerCertPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", getServerKeyPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert_chain", "/nonexistent/path/to/chain.pem"); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, getServerCertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, getServerKeyPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCertChain, "/nonexistent/path/to/chain.pem"); Env env(*this, std::move(cfg)); // Server should fail to start - verify port is 0 - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); BEAST_EXPECT(*grpcPort == 0); // NOLINT(bugprone-unchecked-optional-access) } @@ -748,16 +754,17 @@ public: using namespace jtx; auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", getServerCertPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", getServerKeyPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_client_ca", "/nonexistent/path/to/ca.pem"); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, getServerCertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, getServerKeyPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslClientCa, "/nonexistent/path/to/ca.pem"); Env env(*this, std::move(cfg)); // Server should fail to start - verify port is 0 - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); BEAST_EXPECT(*grpcPort == 0); // NOLINT(bugprone-unchecked-optional-access) } @@ -775,16 +782,17 @@ public: emptyFile.close(); auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", "127.0.0.1"); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", getServerCertPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", getServerKeyPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_client_ca", emptyCAPath.string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, "127.0.0.1"); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, getServerCertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, getServerKeyPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslClientCa, emptyCAPath.string()); Env env(*this, std::move(cfg)); // Server should fail to start due to empty CA file - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); BEAST_EXPECT(*grpcPort == 0); // NOLINT(bugprone-unchecked-optional-access) } @@ -798,18 +806,19 @@ public: // Test with all TLS features enabled: cert, key, cert_chain, and client_ca auto cfg = envconfig(); - (*cfg)[SECTION_PORT_GRPC].set("ip", getEnvLocalhostAddr()); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", getServerCertPath().string()); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", getServerKeyPath().string()); - (*cfg)[SECTION_PORT_GRPC].set( - "ssl_cert_chain", getCACertPath().string()); // Using CA as intermediate - (*cfg)[SECTION_PORT_GRPC].set("ssl_client_ca", getCACertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, getServerCertPath().string()); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, getServerKeyPath().string()); + (*cfg)[Sections::kPortGrpc].set( + Keys::kSslCertChain, getCACertPath().string()); // Using CA as intermediate + (*cfg)[Sections::kPortGrpc].set(Keys::kSslClientCa, getCACertPath().string()); Env env(*this, std::move(cfg)); // Verify the server started successfully - auto const grpcPort = env.app().config()[SECTION_PORT_GRPC].get("port"); + auto const grpcPort = + env.app().config()[Sections::kPortGrpc].get(Keys::kPort); BEAST_EXPECT(grpcPort.has_value()); // NOLINTBEGIN(bugprone-unchecked-optional-access) grpcPort.has_value() checked above BEAST_EXPECT(*grpcPort > 0); diff --git a/src/test/app/HashRouter_test.cpp b/src/test/app/HashRouter_test.cpp index 8f9cae351e..0266da2bc0 100644 --- a/src/test/app/HashRouter_test.cpp +++ b/src/test/app/HashRouter_test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -274,9 +275,9 @@ class HashRouter_test : public beast::unit_test::Suite { Config cfg; // non-default - auto& h = cfg.section("hashrouter"); - h.set("hold_time", "600"); - h.set("relay_time", "15"); + auto& h = cfg.section(Sections::kHashrouter); + h.set(Keys::kHoldTime, "600"); + h.set(Keys::kRelayTime, "15"); auto const setup = setupHashRouter(cfg); BEAST_EXPECT(setup.holdTime == 600s); BEAST_EXPECT(setup.relayTime == 15s); @@ -284,9 +285,9 @@ class HashRouter_test : public beast::unit_test::Suite { Config cfg; // equal - auto& h = cfg.section("hashrouter"); - h.set("hold_time", "400"); - h.set("relay_time", "400"); + auto& h = cfg.section(Sections::kHashrouter); + h.set(Keys::kHoldTime, "400"); + h.set(Keys::kRelayTime, "400"); auto const setup = setupHashRouter(cfg); BEAST_EXPECT(setup.holdTime == 400s); BEAST_EXPECT(setup.relayTime == 400s); @@ -294,9 +295,9 @@ class HashRouter_test : public beast::unit_test::Suite { Config cfg; // wrong order - auto& h = cfg.section("hashrouter"); - h.set("hold_time", "60"); - h.set("relay_time", "120"); + auto& h = cfg.section(Sections::kHashrouter); + h.set(Keys::kHoldTime, "60"); + h.set(Keys::kRelayTime, "120"); try { setupHashRouter(cfg); @@ -313,9 +314,9 @@ class HashRouter_test : public beast::unit_test::Suite { Config cfg; // too small hold - auto& h = cfg.section("hashrouter"); - h.set("hold_time", "10"); - h.set("relay_time", "120"); + auto& h = cfg.section(Sections::kHashrouter); + h.set(Keys::kHoldTime, "10"); + h.set(Keys::kRelayTime, "120"); try { setupHashRouter(cfg); @@ -333,9 +334,9 @@ class HashRouter_test : public beast::unit_test::Suite { Config cfg; // too small relay - auto& h = cfg.section("hashrouter"); - h.set("hold_time", "500"); - h.set("relay_time", "6"); + auto& h = cfg.section(Sections::kHashrouter); + h.set(Keys::kHoldTime, "500"); + h.set(Keys::kRelayTime, "6"); try { setupHashRouter(cfg); @@ -352,9 +353,9 @@ class HashRouter_test : public beast::unit_test::Suite { Config cfg; // garbage - auto& h = cfg.section("hashrouter"); - h.set("hold_time", "alice"); - h.set("relay_time", "bob"); + auto& h = cfg.section(Sections::kHashrouter); + h.set(Keys::kHoldTime, "alice"); + h.set(Keys::kRelayTime, "bob"); auto const setup = setupHashRouter(cfg); // The set function ignores values that don't convert, so the // defaults are left unchanged diff --git a/src/test/app/Manifest_test.cpp b/src/test/app/Manifest_test.cpp index d559ecd7b5..0cf1155cf5 100644 --- a/src/test/app/Manifest_test.cpp +++ b/src/test/app/Manifest_test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -246,7 +247,7 @@ public: auto& app = env.app(); auto unl = std::make_unique( - m, m, env.timeKeeper(), app.config().legacy("database_path"), env.journal); + m, m, env.timeKeeper(), app.config().legacy(Sections::kDatabasePath), env.journal); { // save should not store untrusted master keys to db diff --git a/src/test/app/MultiSign_test.cpp b/src/test/app/MultiSign_test.cpp index f21611df0e..5092cafef9 100644 --- a/src/test/app/MultiSign_test.cpp +++ b/src/test/app/MultiSign_test.cpp @@ -24,11 +24,11 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -496,7 +496,7 @@ public: Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue"); + cfg->loadFromString(std::string("[") + Sections::kSigningSupport + "]\ntrue"); return cfg; }), features); @@ -1308,7 +1308,7 @@ public: Env env( *this, envconfig([](std::unique_ptr cfg) { - cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue"); + cfg->loadFromString(std::string("[") + Sections::kSigningSupport + "]\ntrue"); return cfg; }), features); diff --git a/src/test/app/Regression_test.cpp b/src/test/app/Regression_test.cpp index 1c83e97e61..6ebde20176 100644 --- a/src/test/app/Regression_test.cpp +++ b/src/test/app/Regression_test.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -193,7 +194,7 @@ struct Regression_test : public beast::unit_test::Suite testcase("Autofilled fee should use the escalated fee"); using namespace jtx; Env env(*this, envconfig([](std::unique_ptr cfg) { - cfg->section("transaction_queue").set("minimum_txn_in_ledger_standalone", "3"); + cfg->section(Sections::kTransactionQueue).set(Keys::kMinimumTxnInLedgerStandalone, "3"); cfg->fees.referenceFee = 10; return cfg; })); @@ -233,11 +234,11 @@ struct Regression_test : public beast::unit_test::Suite using namespace std::chrono_literals; Env env(*this, envconfig([](std::unique_ptr cfg) { - auto& s = cfg->section("transaction_queue"); - s.set("minimum_txn_in_ledger_standalone", "4294967295"); - s.set("minimum_txn_in_ledger", "4294967295"); - s.set("target_txn_in_ledger", "4294967295"); - s.set("normal_consensus_increase_percent", "4294967295"); + auto& s = cfg->section(Sections::kTransactionQueue); + s.set(Keys::kMinimumTxnInLedgerStandalone, "4294967295"); + s.set(Keys::kMinimumTxnInLedger, "4294967295"); + s.set(Keys::kTargetTxnInLedger, "4294967295"); + s.set(Keys::kNormalConsensusIncreasePercent, "4294967295"); return cfg; })); diff --git a/src/test/app/SHAMapStore_test.cpp b/src/test/app/SHAMapStore_test.cpp index 6e279eadb2..7a149b2f64 100644 --- a/src/test/app/SHAMapStore_test.cpp +++ b/src/test/app/SHAMapStore_test.cpp @@ -7,11 +7,12 @@ #include #include #include -#include #include #include #include +#include +#include #include #include #include @@ -42,8 +43,8 @@ class SHAMapStore_test : public beast::unit_test::Suite onlineDelete(std::unique_ptr cfg) { cfg->ledgerHistory = kDeleteInterval; - auto& section = cfg->section(ConfigSection::nodeDatabase()); - section.set("online_delete", std::to_string(kDeleteInterval)); + auto& section = cfg->section(Sections::kNodeDatabase); + section.set(Keys::kOnlineDelete, std::to_string(kDeleteInterval)); return cfg; } @@ -51,7 +52,7 @@ class SHAMapStore_test : public beast::unit_test::Suite advisoryDelete(std::unique_ptr cfg) { cfg = onlineDelete(std::move(cfg)); - cfg->section(ConfigSection::nodeDatabase()).set("advisory_delete", "1"); + cfg->section(Sections::kNodeDatabase).set(Keys::kAdvisoryDelete, "1"); return cfg; } @@ -490,13 +491,13 @@ public: std::unique_ptr makeBackendRotating(jtx::Env& env, NodeStoreScheduler& scheduler, std::string path) { - Section section{env.app().config().section(ConfigSection::nodeDatabase())}; + Section section{env.app().config().section(Sections::kNodeDatabase)}; boost::filesystem::path newPath; if (!BEAST_EXPECT(path.size())) return {}; newPath = path; - section.set("path", newPath.string()); + section.set(Keys::kPath, newPath.string()); auto backend{NodeStore::Manager::instance().makeBackend( section, @@ -520,21 +521,21 @@ public: ///////////////////////////////////////////////////////////// // Create NodeStore with two backends to allow online deletion of data. // Normally, SHAMapStoreImp handles all these details. - auto nscfg = env.app().config().section(ConfigSection::nodeDatabase()); + auto nscfg = env.app().config().section(Sections::kNodeDatabase); // Provide default values. - if (!nscfg.exists("cache_size")) + if (!nscfg.exists(Keys::kCacheSize)) { nscfg.set( - "cache_size", + Keys::kCacheSize, std::to_string( env.app().config().getValueFor(SizedItem::TreeCacheSize, std::nullopt))); } - if (!nscfg.exists("cache_age")) + if (!nscfg.exists(Keys::kCacheAge)) { nscfg.set( - "cache_age", + Keys::kCacheAge, std::to_string( env.app().config().getValueFor(SizedItem::TreeCacheAge, std::nullopt))); } diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index dc28388de4..0ae6b4d80a 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -167,7 +168,7 @@ public: using namespace std::chrono; testcase("queue sequence"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto alice = Account("alice"); auto bob = Account("bob"); @@ -380,7 +381,7 @@ public: using namespace jtx; testcase("queue ticket"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto alice = Account("alice"); @@ -618,7 +619,7 @@ public: using namespace jtx; testcase("queue tec"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "2"}})); auto alice = Account("alice"); auto gw = Account("gw"); @@ -655,7 +656,7 @@ public: using namespace std::chrono; testcase("local tx retry"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "2"}})); auto alice = Account("alice"); auto bob = Account("bob"); @@ -708,7 +709,7 @@ public: using namespace std::chrono; testcase("last ledger sequence"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "2"}})); auto alice = Account("alice"); auto bob = Account("bob"); @@ -830,7 +831,7 @@ public: using namespace std::chrono; testcase("zero transaction fee"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "2"}})); auto alice = Account("alice"); auto bob = Account("bob"); @@ -957,7 +958,7 @@ public: using namespace jtx; testcase("queued tx fails"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "2"}})); auto alice = Account("alice"); auto bob = Account("bob"); @@ -1009,8 +1010,8 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "3"}}, - {{"account_reserve", "200"}, {"owner_reserve", "50"}})); + {{Keys::kMinimumTxnInLedgerStandalone, "3"}}, + {{Keys::kAccountReserve, "200"}, {Keys::kOwnerReserve, "50"}})); auto alice = Account("alice"); auto bob = Account("bob"); @@ -1258,7 +1259,7 @@ public: using namespace std::chrono; testcase("tie breaking"); - auto cfg = makeConfig({{"minimum_txn_in_ledger_standalone", "4"}}); + auto cfg = makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "4"}}); cfg->fees.referenceFee = 10; Env env(*this, std::move(cfg)); @@ -1471,7 +1472,7 @@ public: using namespace jtx; testcase("acct tx id"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "1"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "1"}})); auto alice = Account("alice"); @@ -1511,10 +1512,10 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "2"}, - {"minimum_txn_in_ledger", "5"}, - {"target_txn_in_ledger", "4"}, - {"maximum_txn_in_ledger", "5"}})); + {{Keys::kMinimumTxnInLedgerStandalone, "2"}, + {Keys::kMinimumTxnInLedger, "5"}, + {Keys::kTargetTxnInLedger, "4"}, + {Keys::kMaximumTxnInLedger, "5"}})); auto const baseFee = env.current()->fees().base.drops(); auto alice = Account("alice"); @@ -1555,10 +1556,10 @@ public: Env const env( *this, makeConfig( - {{"minimum_txn_in_ledger", "200"}, - {"minimum_txn_in_ledger_standalone", "200"}, - {"target_txn_in_ledger", "4"}, - {"maximum_txn_in_ledger", "5"}})); + {{Keys::kMinimumTxnInLedger, "200"}, + {Keys::kMinimumTxnInLedgerStandalone, "200"}, + {Keys::kTargetTxnInLedger, "4"}, + {Keys::kMaximumTxnInLedger, "5"}})); // should throw fail(); } @@ -1576,10 +1577,10 @@ public: Env const env( *this, makeConfig( - {{"minimum_txn_in_ledger", "200"}, - {"minimum_txn_in_ledger_standalone", "2"}, - {"target_txn_in_ledger", "4"}, - {"maximum_txn_in_ledger", "5"}})); + {{Keys::kMinimumTxnInLedger, "200"}, + {Keys::kMinimumTxnInLedgerStandalone, "2"}, + {Keys::kTargetTxnInLedger, "4"}, + {Keys::kMaximumTxnInLedger, "5"}})); // should throw fail(); } @@ -1597,10 +1598,10 @@ public: Env const env( *this, makeConfig( - {{"minimum_txn_in_ledger", "2"}, - {"minimum_txn_in_ledger_standalone", "200"}, - {"target_txn_in_ledger", "4"}, - {"maximum_txn_in_ledger", "5"}})); + {{Keys::kMinimumTxnInLedger, "2"}, + {Keys::kMinimumTxnInLedgerStandalone, "200"}, + {Keys::kTargetTxnInLedger, "4"}, + {Keys::kMaximumTxnInLedger, "5"}})); // should throw fail(); } @@ -1624,8 +1625,8 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "3"}}, - {{"account_reserve", "200"}, {"owner_reserve", "50"}})); + {{Keys::kMinimumTxnInLedgerStandalone, "3"}}, + {{Keys::kAccountReserve, "200"}, {Keys::kOwnerReserve, "50"}})); auto alice = Account("alice"); auto bob = Account("bob"); @@ -1716,7 +1717,7 @@ public: auto queued = Ter(terQUEUED); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto const baseFee = env.current()->fees().base.drops(); checkMetrics(*this, env, 0, std::nullopt, 0, 3); @@ -1845,7 +1846,7 @@ public: auto queued = Ter(terQUEUED); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto const baseFee = env.current()->fees().base.drops(); checkMetrics(*this, env, 0, std::nullopt, 0, 3); @@ -1996,8 +1997,8 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "3"}}, - {{"account_reserve", "200"}, {"owner_reserve", "50"}})); + {{Keys::kMinimumTxnInLedgerStandalone, "3"}}, + {{Keys::kAccountReserve, "200"}, {Keys::kOwnerReserve, "50"}})); auto alice = Account("alice"); auto charlie = Account("charlie"); @@ -2399,7 +2400,7 @@ public: auto queued = Ter(terQUEUED); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto const baseFee = env.current()->fees().base.drops(); checkMetrics(*this, env, 0, std::nullopt, 0, 3); @@ -2568,9 +2569,9 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "1"}, - {"ledgers_in_queue", "10"}, - {"maximum_txn_per_account", "20"}})); + {{Keys::kMinimumTxnInLedgerStandalone, "1"}, + {Keys::kLedgersInQueue, "10"}, + {Keys::kMaximumTxnPerAccount, "20"}})); auto const baseFee = env.current()->fees().base.drops(); @@ -2650,9 +2651,9 @@ public: testcase("full queue gap handling"); auto cfg = makeConfig( - {{"minimum_txn_in_ledger_standalone", "1"}, - {"ledgers_in_queue", "10"}, - {"maximum_txn_per_account", "11"}}); + {{Keys::kMinimumTxnInLedgerStandalone, "1"}, + {Keys::kLedgersInQueue, "10"}, + {Keys::kMaximumTxnPerAccount, "11"}}); cfg->fees.referenceFee = 10; Env env(*this, std::move(cfg)); @@ -2777,7 +2778,7 @@ public: { testcase("Autofilled sequence should account for TxQ"); using namespace jtx; - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "6"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "6"}})); auto const baseFee = env.current()->fees().base.drops(); EnvSs envs(env); auto const& txQ = env.app().getTxQ(); @@ -2911,7 +2912,7 @@ public: using namespace jtx; testcase("account info"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto const baseFee = env.current()->fees().base.drops(); EnvSs envs(env); @@ -3181,7 +3182,7 @@ public: using namespace jtx; testcase("server info"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto const baseFee = env.current()->fees().base.drops(); EnvSs envs(env); @@ -3407,7 +3408,7 @@ public: using namespace jtx; testcase("server subscribe"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto const baseFee = env.current()->fees().base.drops(); json::Value stream; @@ -3546,7 +3547,7 @@ public: using namespace jtx; testcase("clear queued acct txs"); - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto const baseFee = env.current()->fees().base.drops(); auto alice = Account("alice"); auto bob = Account("bob"); @@ -3756,11 +3757,11 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "3"}, - {"normal_consensus_increase_percent", "25"}, - {"slow_consensus_decrease_percent", "50"}, - {"target_txn_in_ledger", "10"}, - {"maximum_txn_per_account", "200"}})); + {{Keys::kMinimumTxnInLedgerStandalone, "3"}, + {Keys::kNormalConsensusIncreasePercent, "25"}, + {Keys::kSlowConsensusDecreasePercent, "50"}, + {Keys::kTargetTxnInLedger, "10"}, + {Keys::kMaximumTxnPerAccount, "200"}})); auto alice = Account("alice"); checkMetrics(*this, env, 0, std::nullopt, 0, 3); @@ -3842,11 +3843,11 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "3"}, - {"normal_consensus_increase_percent", "150"}, - {"slow_consensus_decrease_percent", "150"}, - {"target_txn_in_ledger", "10"}, - {"maximum_txn_per_account", "200"}})); + {{Keys::kMinimumTxnInLedgerStandalone, "3"}, + {Keys::kNormalConsensusIncreasePercent, "150"}, + {Keys::kSlowConsensusDecreasePercent, "150"}, + {Keys::kTargetTxnInLedger, "10"}, + {Keys::kMaximumTxnPerAccount, "200"}})); auto alice = Account("alice"); checkMetrics(*this, env, 0, std::nullopt, 0, 3); @@ -3899,7 +3900,7 @@ public: testcase("Sequence in queue and open ledger"); using namespace jtx; - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto const alice = Account("alice"); @@ -3962,7 +3963,7 @@ public: testcase("Ticket in queue and open ledger"); using namespace jtx; - Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}})); + Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}})); auto alice = Account("alice"); @@ -4063,15 +4064,16 @@ public: static constexpr int kLedgersInQueue = 30; auto cfg = makeConfig( - {{"minimum_txn_in_ledger_standalone", "1"}, - {"ledgers_in_queue", std::to_string(kLedgersInQueue)}, - {"maximum_txn_per_account", "10"}}, - {{"account_reserve", "1000"}, {"owner_reserve", "50"}}); + {{Keys::kMinimumTxnInLedgerStandalone, "1"}, + {Keys::kLedgersInQueue, std::to_string(kLedgersInQueue)}, + {Keys::kMaximumTxnPerAccount, "10"}}, + {{Keys::kAccountReserve, "1000"}, {Keys::kOwnerReserve, "50"}}); - auto& votingSection = cfg->section("voting"); - votingSection.set("account_reserve", std::to_string(cfg->fees.referenceFee.drops() * 100)); + auto& votingSection = cfg->section(Sections::kVoting); + votingSection.set( + Keys::kAccountReserve, std::to_string(cfg->fees.referenceFee.drops() * 100)); - votingSection.set("reference_fee", std::to_string(cfg->fees.referenceFee.drops())); + votingSection.set(Keys::kReferenceFee, std::to_string(cfg->fees.referenceFee.drops())); Env env(*this, std::move(cfg)); @@ -4228,10 +4230,10 @@ public: Account const fiona("fiona"); auto cfg = makeConfig( - {{"minimum_txn_in_ledger_standalone", "5"}, - {"ledgers_in_queue", "5"}, - {"maximum_txn_per_account", "30"}, - {"minimum_queue_size", "50"}}); + {{Keys::kMinimumTxnInLedgerStandalone, "5"}, + {Keys::kLedgersInQueue, "5"}, + {Keys::kMaximumTxnPerAccount, "30"}, + {Keys::kMinimumQueueSize, "50"}}); Env env(*this, std::move(cfg)); auto const baseFee = env.current()->fees().base.drops(); @@ -4437,10 +4439,10 @@ public: auto usd = gw["USD"]; auto cfg = makeConfig( - {{"minimum_txn_in_ledger_standalone", "5"}, - {"ledgers_in_queue", "5"}, - {"maximum_txn_per_account", "30"}, - {"minimum_queue_size", "50"}}); + {{Keys::kMinimumTxnInLedgerStandalone, "5"}, + {Keys::kLedgersInQueue, "5"}, + {Keys::kMaximumTxnPerAccount, "30"}, + {Keys::kMinimumQueueSize, "50"}}); Env env(*this, std::move(cfg)); @@ -4537,8 +4539,10 @@ public: Env env( *this, makeConfig( - {{"minimum_txn_in_ledger_standalone", "3"}}, - {{"reference_fee", "0"}, {"account_reserve", "0"}, {"owner_reserve", "0"}})); + {{Keys::kMinimumTxnInLedgerStandalone, "3"}}, + {{Keys::kReferenceFee, "0"}, + {Keys::kAccountReserve, "0"}, + {Keys::kOwnerReserve, "0"}})); checkMetrics(*this, env, 0, std::nullopt, 0, 3); diff --git a/src/test/app/ValidatorKeys_test.cpp b/src/test/app/ValidatorKeys_test.cpp index 83267bf0a7..ca0e76c0b3 100644 --- a/src/test/app/ValidatorKeys_test.cpp +++ b/src/test/app/ValidatorKeys_test.cpp @@ -4,11 +4,11 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -100,7 +100,7 @@ public: { // validation seed section -> empty manifest and valid seeds Config c; - c.section(SECTION_VALIDATION_SEED).append(seed_); + c.section(Sections::kValidationSeed).append(seed_); ValidatorKeys k{c, journal}; if (BEAST_EXPECT(k.keys); k.keys.has_value()) @@ -116,7 +116,7 @@ public: { // validation seed bad seed -> invalid Config c; - c.section(SECTION_VALIDATION_SEED).append("badseed"); + c.section(Sections::kValidationSeed).append("badseed"); ValidatorKeys const k{c, journal}; BEAST_EXPECT(k.configInvalid()); @@ -127,7 +127,7 @@ public: { // validator token Config c; - c.section(SECTION_VALIDATOR_TOKEN).append(tokenBlob_); + c.section(Sections::kValidatorToken).append(tokenBlob_); ValidatorKeys k{c, journal}; if (BEAST_EXPECT(k.keys); k.keys.has_value()) @@ -142,7 +142,7 @@ public: { // invalid validator token Config c; - c.section(SECTION_VALIDATOR_TOKEN).append("badtoken"); + c.section(Sections::kValidatorToken).append("badtoken"); ValidatorKeys const k{c, journal}; BEAST_EXPECT(k.configInvalid()); BEAST_EXPECT(!k.keys); @@ -152,8 +152,8 @@ public: { // Cannot specify both Config c; - c.section(SECTION_VALIDATION_SEED).append(seed_); - c.section(SECTION_VALIDATOR_TOKEN).append(tokenBlob_); + c.section(Sections::kValidationSeed).append(seed_); + c.section(Sections::kValidatorToken).append(tokenBlob_); ValidatorKeys const k{c, journal}; BEAST_EXPECT(k.configInvalid()); @@ -164,7 +164,7 @@ public: { // Token manifest and private key must match Config c; - c.section(SECTION_VALIDATOR_TOKEN).append(invalidTokenBlob_); + c.section(Sections::kValidatorToken).append(invalidTokenBlob_); ValidatorKeys const k{c, journal}; BEAST_EXPECT(k.configInvalid()); diff --git a/src/test/app/ValidatorList_test.cpp b/src/test/app/ValidatorList_test.cpp index 80483446a2..d71554f714 100644 --- a/src/test/app/ValidatorList_test.cpp +++ b/src/test/app/ValidatorList_test.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -198,7 +199,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); BEAST_EXPECT(trustedKeys->quorum() == 1); } @@ -208,7 +209,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal, minQuorum); BEAST_EXPECT(trustedKeys->quorum() == minQuorum); @@ -266,7 +267,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); // Correct (empty) configuration @@ -292,7 +293,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); BEAST_EXPECT(trustedKeys->load({}, cfgKeys, emptyCfgPublishers)); @@ -327,7 +328,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); auto const localSigningPublic = @@ -347,7 +348,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); auto const localSigningPublic = randomNode(); @@ -365,7 +366,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) @@ -385,7 +386,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); // load should reject invalid validator list signing keys @@ -421,7 +422,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); std::vector const keys( @@ -446,7 +447,7 @@ private: valManifests, pubManifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); auto const pubRevokedSecret = randomSecretKey(); @@ -485,7 +486,7 @@ private: valManifests, pubManifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); auto const pubRevokedSecret = randomSecretKey(); @@ -571,7 +572,7 @@ private: manifests, manifests, env.app().getTimeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); auto expectTrusted = [this, &trustedKeys](std::vector const& list) { @@ -986,7 +987,7 @@ private: manifests, manifests, env.app().getTimeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); auto const publisherSecret = randomSecretKey(); @@ -1117,7 +1118,7 @@ private: manifestsOuter, manifestsOuter, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); std::vector const cfgPublishersOuter; @@ -1283,7 +1284,7 @@ private: manifestsOuter, manifestsOuter, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); auto const publisherSecret = randomSecretKey(); auto const publisherPublic = derivePublicKey(KeyType::Ed25519, publisherSecret); @@ -1310,7 +1311,7 @@ private: manifestsOuter, manifestsOuter, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); auto const masterPrivate = randomSecretKey(); auto const masterPublic = derivePublicKey(KeyType::Ed25519, masterPrivate); @@ -1344,7 +1345,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal, minQuorum); @@ -1400,7 +1401,7 @@ private: manifestsOuter, manifestsOuter, env.app().getTimeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); std::vector const emptyCfgKeys; @@ -1499,7 +1500,7 @@ private: manifestsOuter, manifestsOuter, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); std::vector const cfgPublishers; @@ -1535,7 +1536,7 @@ private: manifestsOuter, manifestsOuter, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); auto const localKey = randomNode(); @@ -1582,7 +1583,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); hash_set activeValidators; @@ -1670,7 +1671,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); hash_set activeValidators; @@ -1877,7 +1878,7 @@ private: manifests, manifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); // Empty list has no expiration @@ -1899,7 +1900,7 @@ private: manifests, manifests, env.app().getTimeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); std::vector validators = {randomValidator()}; @@ -2040,7 +2041,7 @@ private: manifests, manifests, env.timeKeeper(), - env.app().config().legacy("database_path"), + env.app().config().legacy(Sections::kDatabasePath), env.journal, minimumQuorum); @@ -2636,7 +2637,7 @@ private: valManifests, pubManifests, env.timeKeeper(), - app.config().legacy("database_path"), + app.config().legacy(Sections::kDatabasePath), env.journal); std::vector cfgPublishers; diff --git a/src/test/core/Config_test.cpp b/src/test/core/Config_test.cpp index 92f59fe644..fdac1e450d 100644 --- a/src/test/core/Config_test.cpp +++ b/src/test/core/Config_test.cpp @@ -2,11 +2,11 @@ #include #include -#include -#include #include #include +#include +#include #include // IWYU pragma: keep #include @@ -295,9 +295,9 @@ port_wss_admin c.loadFromString(toLoad); - BEAST_EXPECT(c.legacy("ssl_verify") == "0"); + BEAST_EXPECT(c.legacy(Sections::kSslVerify) == "0"); expectException( - [&c] { [[maybe_unused]] auto _ = c.legacy("server"); }); // not a single line + [&c] { [[maybe_unused]] auto _ = c.legacy(Sections::kServer); }); // not a single line // set a legacy value BEAST_EXPECT(c.legacy("not_in_file").empty()); @@ -329,9 +329,9 @@ port_wss_admin // Load the config file from the current directory and verify it. Config c; c.setup("", true, false, true); - BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values().size() == 1); + BEAST_EXPECT(c.section(Sections::kDebugLogfile).values().size() == 1); BEAST_EXPECT( - c.section(SECTION_DEBUG_LOGFILE).values()[0] == + c.section(Sections::kDebugLogfile).values()[0] == "/Users/dummy/xrpld/config/log/debug.log"); } @@ -368,9 +368,9 @@ port_wss_admin // Load the config file from the config directory and verify it. Config c; c.setup("", true, false, true); - BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values().size() == 1); + BEAST_EXPECT(c.section(Sections::kDebugLogfile).values().size() == 1); BEAST_EXPECT( - c.section(SECTION_DEBUG_LOGFILE).values()[0] == + c.section(Sections::kDebugLogfile).values()[0] == "/Users/dummy/xrpld/config/log/debug.log"); // Restore the environment variables. @@ -404,9 +404,9 @@ port_wss_admin // Load the config file from the config directory and verify it. Config c; c.setup("", true, false, true); - BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values().size() == 1); + BEAST_EXPECT(c.section(Sections::kDebugLogfile).values().size() == 1); BEAST_EXPECT( - c.section(SECTION_DEBUG_LOGFILE).values()[0] == + c.section(Sections::kDebugLogfile).values()[0] == "/Users/dummy/xrpld/config/log/debug.log"); // Restore the environment variables. @@ -436,13 +436,13 @@ port_wss_admin // Dummy test - do we get back what we put in Config c; c.loadFromString(boost::str(cc % dataDirAbs.string())); - BEAST_EXPECT(c.legacy("database_path") == dataDirAbs.string()); + BEAST_EXPECT(c.legacy(Sections::kDatabasePath) == dataDirAbs.string()); } { // Rel paths should convert to abs paths Config c; c.loadFromString(boost::str(cc % dataDirRel.string())); - BEAST_EXPECT(c.legacy("database_path") == dataDirAbs.string()); + BEAST_EXPECT(c.legacy(Sections::kDatabasePath) == dataDirAbs.string()); } { // No db section. @@ -450,7 +450,7 @@ port_wss_admin // load will not. Config c; c.loadFromString(""); - BEAST_EXPECT(c.legacy("database_path").empty()); + BEAST_EXPECT(c.legacy(Sections::kDatabasePath).empty()); } } { @@ -464,7 +464,7 @@ port_wss_admin auto const& c(g.config()); BEAST_EXPECT(g.dataDirExists()); BEAST_EXPECT(g.configFileExists()); - BEAST_EXPECT(c.legacy("database_path") == dataDirAbs.string()); + BEAST_EXPECT(c.legacy(Sections::kDatabasePath) == dataDirAbs.string()); } { // read from file relative path @@ -474,7 +474,7 @@ port_wss_admin std::string const nativeDbPath = absolute(path(dbPath)).string(); BEAST_EXPECT(g.dataDirExists()); BEAST_EXPECT(g.configFileExists()); - BEAST_EXPECT(c.legacy("database_path") == nativeDbPath); + BEAST_EXPECT(c.legacy(Sections::kDatabasePath) == nativeDbPath); } { // read from file no path @@ -484,7 +484,7 @@ port_wss_admin absolute(g.subdir() / path(Config::kDatabaseDirName)).string(); BEAST_EXPECT(g.dataDirExists()); BEAST_EXPECT(g.configFileExists()); - BEAST_EXPECT(c.legacy("database_path") == nativeDbPath); + BEAST_EXPECT(c.legacy(Sections::kDatabasePath) == nativeDbPath); } } @@ -653,8 +653,8 @@ nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5 nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.legacy("validators_file").empty()); - BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 5); + BEAST_EXPECT(c.legacy(Sections::kValidatorsFile).empty()); + BEAST_EXPECT(c.section(Sections::kValidators).values().size() == 5); BEAST_EXPECT(c.validatorListThreshold == std::nullopt); } { @@ -672,19 +672,19 @@ trust-these-validators.gov 1 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 2); BEAST_EXPECT( - c.section(SECTION_VALIDATOR_LIST_SITES).values()[0] == "xrpl-validators.com"); + c.section(Sections::kValidatorListSites).values()[0] == "xrpl-validators.com"); BEAST_EXPECT( - c.section(SECTION_VALIDATOR_LIST_SITES).values()[1] == + c.section(Sections::kValidatorListSites).values()[1] == "trust-these-validators.gov"); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 1); + BEAST_EXPECT(c.section(Sections::kValidatorListKeys).values().size() == 1); BEAST_EXPECT( - c.section(SECTION_VALIDATOR_LIST_KEYS).values()[0] == + c.section(Sections::kValidatorListKeys).values()[0] == "021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801" "E566"); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values()[0] == "1"); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values().size() == 1); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values()[0] == "1"); BEAST_EXPECT(c.validatorListThreshold == std::size_t(1)); } { @@ -702,19 +702,19 @@ trust-these-validators.gov 0 )xrpldConfig"); c.loadFromString(toLoad); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 2); BEAST_EXPECT( - c.section(SECTION_VALIDATOR_LIST_SITES).values()[0] == "xrpl-validators.com"); + c.section(Sections::kValidatorListSites).values()[0] == "xrpl-validators.com"); BEAST_EXPECT( - c.section(SECTION_VALIDATOR_LIST_SITES).values()[1] == + c.section(Sections::kValidatorListSites).values()[1] == "trust-these-validators.gov"); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 1); + BEAST_EXPECT(c.section(Sections::kValidatorListKeys).values().size() == 1); BEAST_EXPECT( - c.section(SECTION_VALIDATOR_LIST_KEYS).values()[0] == + c.section(Sections::kValidatorListKeys).values()[0] == "021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801" "E566"); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values()[0] == "0"); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values().size() == 1); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values()[0] == "0"); BEAST_EXPECT(c.validatorListThreshold == std::nullopt); } { @@ -831,11 +831,11 @@ trust-these-validators.gov Config c; boost::format cc("[validators_file]\n%1%\n"); c.loadFromString(boost::str(cc % vtg.validatorsFile())); - BEAST_EXPECT(c.legacy("validators_file") == vtg.validatorsFile()); - BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); + BEAST_EXPECT(c.legacy(Sections::kValidatorsFile) == vtg.validatorsFile()); + BEAST_EXPECT(c.section(Sections::kValidators).values().size() == 8); + BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListKeys).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values().size() == 1); BEAST_EXPECT(c.validatorListThreshold == 2); } { @@ -848,11 +848,11 @@ trust-these-validators.gov BEAST_EXPECT(vtg.validatorsFileExists()); BEAST_EXPECT(rcg.configFileExists()); auto const& c(rcg.config()); - BEAST_EXPECT(c.legacy("validators_file") == valFileName); - BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); + BEAST_EXPECT(c.legacy(Sections::kValidatorsFile) == valFileName); + BEAST_EXPECT(c.section(Sections::kValidators).values().size() == 8); + BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListKeys).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values().size() == 1); BEAST_EXPECT(c.validatorListThreshold == 2); } { @@ -865,11 +865,11 @@ trust-these-validators.gov BEAST_EXPECT(vtg.validatorsFileExists()); BEAST_EXPECT(rcg.configFileExists()); auto const& c(rcg.config()); - BEAST_EXPECT(c.legacy("validators_file") == valFilePath); - BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); + BEAST_EXPECT(c.legacy(Sections::kValidatorsFile) == valFilePath); + BEAST_EXPECT(c.section(Sections::kValidators).values().size() == 8); + BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListKeys).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values().size() == 1); BEAST_EXPECT(c.validatorListThreshold == 2); } { @@ -880,11 +880,11 @@ trust-these-validators.gov BEAST_EXPECT(vtg.validatorsFileExists()); BEAST_EXPECT(rcg.configFileExists()); auto const& c(rcg.config()); - BEAST_EXPECT(c.legacy("validators_file").empty()); - BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); + BEAST_EXPECT(c.legacy(Sections::kValidatorsFile).empty()); + BEAST_EXPECT(c.section(Sections::kValidators).values().size() == 8); + BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListKeys).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values().size() == 1); BEAST_EXPECT(c.validatorListThreshold == 2); } { @@ -899,11 +899,11 @@ trust-these-validators.gov *this, vtg.subdir(), "", Config::kConfigFileName, vtg.validatorsFile(), false); BEAST_EXPECT(rcg.configFileExists()); auto const& c(rcg.config()); - BEAST_EXPECT(c.legacy("validators_file") == vtg.validatorsFile()); - BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); + BEAST_EXPECT(c.legacy(Sections::kValidatorsFile) == vtg.validatorsFile()); + BEAST_EXPECT(c.section(Sections::kValidators).values().size() == 8); + BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListKeys).values().size() == 2); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values().size() == 1); BEAST_EXPECT(c.validatorListThreshold == 2); } @@ -935,11 +935,11 @@ trust-these-validators.gov BEAST_EXPECT(vtg.validatorsFileExists()); Config c; c.loadFromString(boost::str(cc % vtg.validatorsFile())); - BEAST_EXPECT(c.legacy("validators_file") == vtg.validatorsFile()); - BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 15); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 4); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 3); - BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1); + BEAST_EXPECT(c.legacy(Sections::kValidatorsFile) == vtg.validatorsFile()); + BEAST_EXPECT(c.section(Sections::kValidators).values().size() == 15); + BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 4); + BEAST_EXPECT(c.section(Sections::kValidatorListKeys).values().size() == 3); + BEAST_EXPECT(c.section(Sections::kValidatorListThreshold).values().size() == 1); BEAST_EXPECT(c.validatorListThreshold == 2); } { @@ -1018,7 +1018,7 @@ trust-these-validators.gov BEAST_EXPECT(!config.silent()); BEAST_EXPECT(!config.standalone()); BEAST_EXPECT(config.ledgerHistory == 256); - BEAST_EXPECT(!config.legacy("database_path").empty()); + BEAST_EXPECT(!config.legacy(Sections::kDatabasePath).empty()); } { Config config; @@ -1031,7 +1031,7 @@ trust-these-validators.gov BEAST_EXPECT(!config.silent()); BEAST_EXPECT(!config.standalone()); BEAST_EXPECT(config.ledgerHistory == 256); - BEAST_EXPECT(!config.legacy("database_path").empty()); + BEAST_EXPECT(!config.legacy(Sections::kDatabasePath).empty()); } { Config config; @@ -1044,7 +1044,7 @@ trust-these-validators.gov BEAST_EXPECT(config.silent()); BEAST_EXPECT(!config.standalone()); BEAST_EXPECT(config.ledgerHistory == 256); - BEAST_EXPECT(!config.legacy("database_path").empty()); + BEAST_EXPECT(!config.legacy(Sections::kDatabasePath).empty()); } { Config config; @@ -1057,7 +1057,7 @@ trust-these-validators.gov BEAST_EXPECT(config.silent()); BEAST_EXPECT(!config.standalone()); BEAST_EXPECT(config.ledgerHistory == 256); - BEAST_EXPECT(!config.legacy("database_path").empty()); + BEAST_EXPECT(!config.legacy(Sections::kDatabasePath).empty()); } { Config config; @@ -1070,7 +1070,7 @@ trust-these-validators.gov BEAST_EXPECT(!config.silent()); BEAST_EXPECT(config.standalone()); BEAST_EXPECT(config.ledgerHistory == 0); - BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath); + BEAST_EXPECT(config.legacy(Sections::kDatabasePath).empty() == !explicitPath); } { Config config; @@ -1083,7 +1083,7 @@ trust-these-validators.gov BEAST_EXPECT(!config.silent()); BEAST_EXPECT(config.standalone()); BEAST_EXPECT(config.ledgerHistory == 0); - BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath); + BEAST_EXPECT(config.legacy(Sections::kDatabasePath).empty() == !explicitPath); } { Config config; @@ -1096,7 +1096,7 @@ trust-these-validators.gov BEAST_EXPECT(config.silent()); BEAST_EXPECT(config.standalone()); BEAST_EXPECT(config.ledgerHistory == 0); - BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath); + BEAST_EXPECT(config.legacy(Sections::kDatabasePath).empty() == !explicitPath); } { Config config; @@ -1109,7 +1109,7 @@ trust-these-validators.gov BEAST_EXPECT(config.silent()); BEAST_EXPECT(config.standalone()); BEAST_EXPECT(config.ledgerHistory == 0); - BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath); + BEAST_EXPECT(config.legacy(Sections::kDatabasePath).empty() == !explicitPath); } } @@ -1118,16 +1118,16 @@ trust-these-validators.gov { detail::FileCfgGuard const cfg(*this, "testPort", "", Config::kConfigFileName, ""); auto const& conf = cfg.config(); - if (!BEAST_EXPECT(conf.exists("port_rpc"))) + if (!BEAST_EXPECT(conf.exists(Sections::kPortRpc))) return; - if (!BEAST_EXPECT(conf.exists("port_wss_admin"))) + if (!BEAST_EXPECT(conf.exists(Sections::kPortWssAdmin))) return; ParsedPort rpc; - if (!unexcept([&]() { parsePort(rpc, conf["port_rpc"], log); })) + if (!unexcept([&]() { parsePort(rpc, conf[Sections::kPortRpc], log); })) return; BEAST_EXPECT(rpc.adminNetsV4.size() + rpc.adminNetsV6.size() == 2); ParsedPort wss; - if (!unexcept([&]() { parsePort(wss, conf["port_wss_admin"], log); })) + if (!unexcept([&]() { parsePort(wss, conf[Sections::kPortWssAdmin], log); })) return; BEAST_EXPECT(wss.adminNetsV4.size() + wss.adminNetsV6.size() == 1); } @@ -1182,14 +1182,15 @@ r.ripple.com 51235 )"); cfg.loadFromString(toLoad); BEAST_EXPECT( - cfg.exists("port_rpc") && cfg.section("port_rpc").lines().empty() && - cfg.section("port_rpc").values().empty()); + cfg.exists(Sections::kPortRpc) && cfg.section(Sections::kPortRpc).lines().empty() && + cfg.section(Sections::kPortRpc).values().empty()); BEAST_EXPECT( - cfg.exists(SECTION_IPS) && cfg.section(SECTION_IPS).lines().size() == 1 && - cfg.section(SECTION_IPS).values().size() == 1); + cfg.exists(Sections::kIps) && cfg.section(Sections::kIps).lines().size() == 1 && + cfg.section(Sections::kIps).values().size() == 1); BEAST_EXPECT( - cfg.exists(SECTION_IPS_FIXED) && cfg.section(SECTION_IPS_FIXED).lines().size() == 2 && - cfg.section(SECTION_IPS_FIXED).values().size() == 2); + cfg.exists(Sections::kIpsFixed) && + cfg.section(Sections::kIpsFixed).lines().size() == 2 && + cfg.section(Sections::kIpsFixed).values().size() == 2); } void @@ -1237,14 +1238,15 @@ r.ripple.com:51235 )"); cfg.loadFromString(toLoad); BEAST_EXPECT( - cfg.exists("port_rpc") && cfg.section("port_rpc").lines().empty() && - cfg.section("port_rpc").values().empty()); + cfg.exists(Sections::kPortRpc) && cfg.section(Sections::kPortRpc).lines().empty() && + cfg.section(Sections::kPortRpc).values().empty()); BEAST_EXPECT( - cfg.exists(SECTION_IPS) && cfg.section(SECTION_IPS).lines().size() == 1 && - cfg.section(SECTION_IPS).values().size() == 1); + cfg.exists(Sections::kIps) && cfg.section(Sections::kIps).lines().size() == 1 && + cfg.section(Sections::kIps).values().size() == 1); BEAST_EXPECT( - cfg.exists(SECTION_IPS_FIXED) && cfg.section(SECTION_IPS_FIXED).lines().size() == 15 && - cfg.section(SECTION_IPS_FIXED).values().size() == 15); + cfg.exists(Sections::kIpsFixed) && + cfg.section(Sections::kIpsFixed).lines().size() == 15 && + cfg.section(Sections::kIpsFixed).values().size() == 15); BEAST_EXPECT(cfg.ips[0] == "r.ripple.com 51235"); BEAST_EXPECT(cfg.ipsFixed[0] == "s1.ripple.com 51235"); @@ -1335,18 +1337,18 @@ r.ripple.com:51235 Section s; s.append("online_delete = 3000"); std::uint32_t od = 0; - BEAST_EXPECT(set(od, "online_delete", s)); + BEAST_EXPECT(set(od, Keys::kOnlineDelete, s)); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECTS(od == 3000, *(s.get("online_delete"))); + BEAST_EXPECTS(od == 3000, *(s.get(Keys::kOnlineDelete))); } { Section s; s.append("online_delete = 2000 #my comment on this"); std::uint32_t od = 0; - BEAST_EXPECT(set(od, "online_delete", s)); + BEAST_EXPECT(set(od, Keys::kOnlineDelete, s)); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECTS(od == 2000, *(s.get("online_delete"))); + BEAST_EXPECTS(od == 2000, *(s.get(Keys::kOnlineDelete))); } } diff --git a/src/test/core/SociDB_test.cpp b/src/test/core/SociDB_test.cpp index f4c6fb04f1..57ff19fec5 100644 --- a/src/test/core/SociDB_test.cpp +++ b/src/test/core/SociDB_test.cpp @@ -1,8 +1,9 @@ #include -#include #include #include +#include +#include #include #include @@ -32,10 +33,10 @@ private: static void setupSQLiteConfig(BasicConfig& config, boost::filesystem::path const& dbPath) { - config.overwrite("sqdb", "backend", "sqlite"); + config.overwrite(Sections::kSqdb, Keys::kBackend, "sqlite"); auto value = dbPath.string(); if (!value.empty()) - config.legacy("database_path", value); + config.legacy(Sections::kDatabasePath, value); } static void diff --git a/src/test/jtx/Env_test.cpp b/src/test/jtx/Env_test.cpp index 0c9e5ffe24..d82cb86b36 100644 --- a/src/test/jtx/Env_test.cpp +++ b/src/test/jtx/Env_test.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -863,7 +864,7 @@ public: jtx::Env const env{ *this, jtx::envconfig([](std::unique_ptr cfg) { - (*cfg).deprecatedClearSection("port_rpc"); + (*cfg).deprecatedClearSection(Sections::kPortRpc); return cfg; }), nullptr, diff --git a/src/test/jtx/envconfig.h b/src/test/jtx/envconfig.h index 9a4e1dc20a..4c56ec8217 100644 --- a/src/test/jtx/envconfig.h +++ b/src/test/jtx/envconfig.h @@ -4,11 +4,6 @@ namespace xrpl::test { -// frequently used macros defined here for convenience. -#define PORT_WS "port_ws" -#define PORT_RPC "port_rpc" -#define PORT_PEER "port_peer" - extern std::atomic gEnvUseIPv4; inline char const* diff --git a/src/test/jtx/impl/JSONRPCClient.cpp b/src/test/jtx/impl/JSONRPCClient.cpp index cf81bfab0c..495fc5a657 100644 --- a/src/test/jtx/impl/JSONRPCClient.cpp +++ b/src/test/jtx/impl/JSONRPCClient.cpp @@ -4,8 +4,9 @@ #include -#include #include +#include +#include #include #include #include @@ -40,8 +41,8 @@ class JSONRPCClient : public AbstractClient { auto& log = std::cerr; ParsedPort common; - parsePort(common, cfg["server"], log); - for (auto const& name : cfg.section("server").values()) + parsePort(common, cfg[Sections::kServer], log); + for (auto const& name : cfg.section(Sections::kServer).values()) { if (!cfg.exists(name)) continue; diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index 6d069523d2..c00a88f270 100644 --- a/src/test/jtx/impl/WSClient.cpp +++ b/src/test/jtx/impl/WSClient.cpp @@ -2,8 +2,9 @@ #include -#include #include +#include +#include #include #include #include @@ -62,9 +63,9 @@ class WSClientImpl : public WSClient { auto& log = std::cerr; ParsedPort common; - parsePort(common, cfg["server"], log); + parsePort(common, cfg[Sections::kServer], log); auto const ps = v2 ? "ws2" : "ws"; - for (auto const& name : cfg.section("server").values()) + for (auto const& name : cfg.section(Sections::kServer).values()) { if (!cfg.exists(name)) continue; diff --git a/src/test/jtx/impl/envconfig.cpp b/src/test/jtx/impl/envconfig.cpp index 56197e1078..bc65738b44 100644 --- a/src/test/jtx/impl/envconfig.cpp +++ b/src/test/jtx/impl/envconfig.cpp @@ -3,7 +3,8 @@ #include #include -#include + +#include #include #include @@ -27,33 +28,33 @@ setupConfigForUnitTests(Config& cfg) // The Beta API (currently v2) is always available to tests cfg.betaRpcApi = true; - cfg.overwrite(ConfigSection::nodeDatabase(), "type", "memory"); - cfg.overwrite(ConfigSection::nodeDatabase(), "path", "main"); - cfg.deprecatedClearSection(ConfigSection::importNodeDatabase()); - cfg.legacy("database_path", ""); + cfg.overwrite(Sections::kNodeDatabase, Keys::kType, "memory"); + cfg.overwrite(Sections::kNodeDatabase, Keys::kPath, "main"); + cfg.deprecatedClearSection(Sections::kImportNodeDatabase); + cfg.legacy(Sections::kDatabasePath, ""); cfg.setupControl(true, true, true); - cfg["server"].append(PORT_PEER); - cfg[PORT_PEER].set("ip", getEnvLocalhostAddr()); + cfg[Sections::kServer].append(Sections::kPortPeer); + cfg[Sections::kPortPeer].set(Keys::kIp, getEnvLocalhostAddr()); // Using port 0 asks the operating system to allocate an unused port, which // can be obtained after a "bind" call. // Works for all system (Linux, Windows, Unix, Mac). // Check https://man7.org/linux/man-pages/man7/ip.7.html // "ip_local_port_range" section for more info - cfg[PORT_PEER].set("port", "0"); - cfg[PORT_PEER].set("protocol", "peer"); + cfg[Sections::kPortPeer].set(Keys::kPort, "0"); + cfg[Sections::kPortPeer].set(Keys::kProtocol, "peer"); - cfg["server"].append(PORT_RPC); - cfg[PORT_RPC].set("ip", getEnvLocalhostAddr()); - cfg[PORT_RPC].set("admin", getEnvLocalhostAddr()); - cfg[PORT_RPC].set("port", "0"); - cfg[PORT_RPC].set("protocol", "http,ws2"); + cfg[Sections::kServer].append(Sections::kPortRpc); + cfg[Sections::kPortRpc].set(Keys::kIp, getEnvLocalhostAddr()); + cfg[Sections::kPortRpc].set(Keys::kAdmin, getEnvLocalhostAddr()); + cfg[Sections::kPortRpc].set(Keys::kPort, "0"); + cfg[Sections::kPortRpc].set(Keys::kProtocol, "http,ws2"); - cfg["server"].append(PORT_WS); - cfg[PORT_WS].set("ip", getEnvLocalhostAddr()); - cfg[PORT_WS].set("admin", getEnvLocalhostAddr()); - cfg[PORT_WS].set("port", "0"); - cfg[PORT_WS].set("protocol", "ws"); + cfg[Sections::kServer].append(Sections::kPortWs); + cfg[Sections::kPortWs].set(Keys::kIp, getEnvLocalhostAddr()); + cfg[Sections::kPortWs].set(Keys::kAdmin, getEnvLocalhostAddr()); + cfg[Sections::kPortWs].set(Keys::kPort, "0"); + cfg[Sections::kPortWs].set(Keys::kProtocol, "ws"); cfg.sslVerify = false; } @@ -62,35 +63,35 @@ namespace jtx { std::unique_ptr noAdmin(std::unique_ptr cfg) { - (*cfg)[PORT_RPC].set("admin", ""); - (*cfg)[PORT_WS].set("admin", ""); + (*cfg)[Sections::kPortRpc].set(Keys::kAdmin, ""); + (*cfg)[Sections::kPortWs].set(Keys::kAdmin, ""); return cfg; } std::unique_ptr secureGateway(std::unique_ptr cfg) { - (*cfg)[PORT_RPC].set("admin", ""); - (*cfg)[PORT_WS].set("admin", ""); - (*cfg)[PORT_RPC].set("secure_gateway", getEnvLocalhostAddr()); + (*cfg)[Sections::kPortRpc].set(Keys::kAdmin, ""); + (*cfg)[Sections::kPortWs].set(Keys::kAdmin, ""); + (*cfg)[Sections::kPortRpc].set(Keys::kSecureGateway, getEnvLocalhostAddr()); return cfg; } std::unique_ptr adminLocalnet(std::unique_ptr cfg) { - (*cfg)[PORT_RPC].set("admin", "127.0.0.0/8"); - (*cfg)[PORT_WS].set("admin", "127.0.0.0/8"); + (*cfg)[Sections::kPortRpc].set(Keys::kAdmin, "127.0.0.0/8"); + (*cfg)[Sections::kPortWs].set(Keys::kAdmin, "127.0.0.0/8"); return cfg; } std::unique_ptr secureGatewayLocalnet(std::unique_ptr cfg) { - (*cfg)[PORT_RPC].set("admin", ""); - (*cfg)[PORT_WS].set("admin", ""); - (*cfg)[PORT_RPC].set("secure_gateway", "127.0.0.0/8"); - (*cfg)[PORT_WS].set("secure_gateway", "127.0.0.0/8"); + (*cfg)[Sections::kPortRpc].set(Keys::kAdmin, ""); + (*cfg)[Sections::kPortWs].set(Keys::kAdmin, ""); + (*cfg)[Sections::kPortRpc].set(Keys::kSecureGateway, "127.0.0.0/8"); + (*cfg)[Sections::kPortWs].set(Keys::kSecureGateway, "127.0.0.0/8"); return cfg; } std::unique_ptr @@ -106,7 +107,7 @@ std::unique_ptr validator(std::unique_ptr cfg, std::string const& seed) { // If the config has valid validation keys then we run as a validator. - cfg->section(SECTION_VALIDATION_SEED) + cfg->section(Sections::kValidationSeed) .append(std::vector{seed.empty() ? kDefaultSeed : seed}); return cfg; } @@ -114,20 +115,20 @@ validator(std::unique_ptr cfg, std::string const& seed) std::unique_ptr addGrpcConfig(std::unique_ptr cfg) { - (*cfg)[SECTION_PORT_GRPC].set("ip", getEnvLocalhostAddr()); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); return cfg; } std::unique_ptr addGrpcConfigWithSecureGateway(std::unique_ptr cfg, std::string const& secureGateway) { - (*cfg)[SECTION_PORT_GRPC].set("ip", getEnvLocalhostAddr()); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, getEnvLocalhostAddr()); // Check https://man7.org/linux/man-pages/man7/ip.7.html // "ip_local_port_range" section for using 0 ports - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("secure_gateway", secureGateway); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSecureGateway, secureGateway); return cfg; } @@ -137,10 +138,10 @@ addGrpcConfigWithTLS( std::string const& certPath, std::string const& keyPath) { - (*cfg)[SECTION_PORT_GRPC].set("ip", getEnvLocalhostAddr()); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", certPath); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", keyPath); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, certPath); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, keyPath); return cfg; } @@ -151,11 +152,11 @@ addGrpcConfigWithTLSAndClientCA( std::string const& keyPath, std::string const& clientCAPath) { - (*cfg)[SECTION_PORT_GRPC].set("ip", getEnvLocalhostAddr()); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", certPath); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", keyPath); - (*cfg)[SECTION_PORT_GRPC].set("ssl_client_ca", clientCAPath); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, certPath); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, keyPath); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslClientCa, clientCAPath); return cfg; } @@ -166,11 +167,11 @@ addGrpcConfigWithTLSAndCertChain( std::string const& keyPath, std::string const& certChainPath) { - (*cfg)[SECTION_PORT_GRPC].set("ip", getEnvLocalhostAddr()); - (*cfg)[SECTION_PORT_GRPC].set("port", "0"); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert", certPath); - (*cfg)[SECTION_PORT_GRPC].set("ssl_key", keyPath); - (*cfg)[SECTION_PORT_GRPC].set("ssl_cert_chain", certChainPath); + (*cfg)[Sections::kPortGrpc].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortGrpc].set(Keys::kPort, "0"); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCert, certPath); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslKey, keyPath); + (*cfg)[Sections::kPortGrpc].set(Keys::kSslCertChain, certChainPath); return cfg; } @@ -180,13 +181,13 @@ makeConfig( std::map extraVoting) { auto p = test::jtx::envconfig(); - auto& section = p->section("transaction_queue"); - section.set("ledgers_in_queue", "2"); - section.set("minimum_queue_size", "2"); - section.set("min_ledgers_to_compute_size_limit", "3"); - section.set("max_ledger_counts_to_store", "100"); - section.set("retry_sequence_percent", "25"); - section.set("normal_consensus_increase_percent", "0"); + auto& section = p->section(Sections::kTransactionQueue); + section.set(Keys::kLedgersInQueue, "2"); + section.set(Keys::kMinimumQueueSize, "2"); + section.set(Keys::kMinLedgersToComputeSizeLimit, "3"); + section.set(Keys::kMaxLedgerCountsToStore, "100"); + section.set(Keys::kRetrySequencePercent, "25"); + section.set(Keys::kNormalConsensusIncreasePercent, "0"); for (auto const& [k, v] : extraTxQ) section.set(k, v); @@ -195,14 +196,14 @@ makeConfig( // a FeeVote if (!extraVoting.empty()) { - auto& votingSection = p->section("voting"); + auto& votingSection = p->section(Sections::kVoting); for (auto const& [k, v] : extraVoting) { votingSection.set(k, v); } // In order for the vote to occur, we must run as a validator - p->section("validation_seed").legacy("shUwVw52ofnCUX5m7kPTKzJdr4HEH"); + p->section(Sections::kValidationSeed).legacy("shUwVw52ofnCUX5m7kPTKzJdr4HEH"); } return p; } diff --git a/src/test/nodestore/Backend_test.cpp b/src/test/nodestore/Backend_test.cpp index 32b7d46868..65601b0cf5 100644 --- a/src/test/nodestore/Backend_test.cpp +++ b/src/test/nodestore/Backend_test.cpp @@ -1,12 +1,13 @@ #include #include -#include #include #include #include #include #include +#include +#include #include #include #include @@ -33,8 +34,8 @@ public: Section params; beast::TempDir const tempDir; - params.set("type", type); - params.set("path", tempDir.path()); + params.set(Keys::kType, type); + params.set(Keys::kPath, tempDir.path()); beast::xor_shift_engine rng(seedValue); diff --git a/src/test/nodestore/Database_test.cpp b/src/test/nodestore/Database_test.cpp index 50bdfefd4c..bb8ec7d4fd 100644 --- a/src/test/nodestore/Database_test.cpp +++ b/src/test/nodestore/Database_test.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -71,8 +73,8 @@ public: Env env = [&]() { auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("safety_level", "high"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kSafetyLevel, "high"); } p->ledgerHistory = 100'000'000; @@ -100,8 +102,8 @@ public: Env env = [&]() { auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("safety_level", "low"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kSafetyLevel, "low"); } p->ledgerHistory = 100'000'000; @@ -129,10 +131,10 @@ public: Env env = [&]() { auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("journal_mode", "off"); - section.set("synchronous", "extra"); - section.set("temp_store", "default"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kJournalMode, "off"); + section.set(Keys::kSynchronous, "extra"); + section.set(Keys::kTempStore, "default"); } return Env( @@ -161,10 +163,10 @@ public: Env env = [&]() { auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("journal_mode", "off"); - section.set("synchronous", "extra"); - section.set("temp_store", "default"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kJournalMode, "off"); + section.set(Keys::kSynchronous, "extra"); + section.set(Keys::kTempStore, "default"); } p->ledgerHistory = 50'000'000; @@ -197,11 +199,11 @@ public: auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("safety_level", "low"); - section.set("journal_mode", "off"); - section.set("synchronous", "extra"); - section.set("temp_store", "default"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kSafetyLevel, "low"); + section.set(Keys::kJournalMode, "off"); + section.set(Keys::kSynchronous, "extra"); + section.set(Keys::kTempStore, "default"); } try @@ -228,9 +230,9 @@ public: auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("safety_level", "high"); - section.set("journal_mode", "off"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kSafetyLevel, "high"); + section.set(Keys::kJournalMode, "off"); } try @@ -257,9 +259,9 @@ public: auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("safety_level", "low"); - section.set("synchronous", "extra"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kSafetyLevel, "low"); + section.set(Keys::kSynchronous, "extra"); } try @@ -286,9 +288,9 @@ public: auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("safety_level", "high"); - section.set("temp_store", "default"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kSafetyLevel, "high"); + section.set(Keys::kTempStore, "default"); } try @@ -315,8 +317,8 @@ public: auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("safety_level", "slow"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kSafetyLevel, "slow"); } try @@ -343,8 +345,8 @@ public: auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("journal_mode", "fast"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kJournalMode, "fast"); } try @@ -371,8 +373,8 @@ public: auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("synchronous", "instant"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kSynchronous, "instant"); } try @@ -399,8 +401,8 @@ public: auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("temp_store", "network"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kTempStore, "network"); } try @@ -434,9 +436,9 @@ public: Env env = [&]() { auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("page_size", "512"); - section.set("journal_size_limit", "2582080"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kPageSize, "512"); + section.set(Keys::kJournalSizeLimit, "2582080"); } return Env(*this, std::move(p)); }(); @@ -455,8 +457,8 @@ public: bool found = false; auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("page_size", "256"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kPageSize, "256"); } try { @@ -478,8 +480,8 @@ public: bool found = false; auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("page_size", "131072"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kPageSize, "131072"); } try { @@ -501,8 +503,8 @@ public: bool found = false; auto p = test::jtx::envconfig(); { - auto& section = p->section("sqlite"); - section.set("page_size", "513"); + auto& section = p->section(Sections::kSqlite); + section.set(Keys::kPageSize, "513"); } try { @@ -532,8 +534,8 @@ public: beast::TempDir const nodeDb; Section srcParams; - srcParams.set("type", srcBackendType); - srcParams.set("path", nodeDb.path()); + srcParams.set(Keys::kType, srcBackendType); + srcParams.set(Keys::kPath, nodeDb.path()); // Create a batch auto batch = createPredictableBatch(kNumObjectsToTest, seedValue); @@ -555,8 +557,8 @@ public: // Set up the destination database beast::TempDir const destDb; Section destParams; - destParams.set("type", destBackendType); - destParams.set("path", destDb.path()); + destParams.set(Keys::kType, destBackendType); + destParams.set(Keys::kPath, destDb.path()); std::unique_ptr dest = Manager::instance().makeDatabase(megabytes(4), scheduler, 2, destParams, journal_); @@ -593,8 +595,8 @@ public: beast::TempDir const nodeDb; Section nodeParams; - nodeParams.set("type", type); - nodeParams.set("path", nodeDb.path()); + nodeParams.set(Keys::kType, type); + nodeParams.set(Keys::kPath, nodeDb.path()); beast::xor_shift_engine rng(seedValue); @@ -653,7 +655,7 @@ public: // Set an invalid earliest ledger sequence try { - nodeParams.set("earliest_seq", "0"); + nodeParams.set(Keys::kEarliestSeq, "0"); std::unique_ptr const db = Manager::instance().makeDatabase( megabytes(4), scheduler, 2, nodeParams, journal_); } @@ -664,7 +666,7 @@ public: { // Set a valid earliest ledger sequence - nodeParams.set("earliest_seq", "1"); + nodeParams.set(Keys::kEarliestSeq, "1"); std::unique_ptr db = Manager::instance().makeDatabase( megabytes(4), scheduler, 2, nodeParams, journal_); @@ -676,7 +678,7 @@ public: try { // Set to default earliest ledger sequence - nodeParams.set("earliest_seq", std::to_string(kXrpLedgerEarliestSeq)); + nodeParams.set(Keys::kEarliestSeq, std::to_string(kXrpLedgerEarliestSeq)); std::unique_ptr const db2 = Manager::instance().makeDatabase( megabytes(4), scheduler, 2, nodeParams, journal_); } diff --git a/src/test/nodestore/NuDBFactory_test.cpp b/src/test/nodestore/NuDBFactory_test.cpp index fae13b9cc8..3ca3fa6838 100644 --- a/src/test/nodestore/NuDBFactory_test.cpp +++ b/src/test/nodestore/NuDBFactory_test.cpp @@ -1,12 +1,13 @@ #include #include -#include #include #include #include #include #include +#include +#include #include #include #include @@ -29,10 +30,10 @@ private: createSection(std::string const& path, std::string const& blockSize = "") { Section params; - params.set("type", "nudb"); - params.set("path", path); + params.set(Keys::kType, "nudb"); + params.set(Keys::kPath, path); if (!blockSize.empty()) - params.set("nudb_block_size", blockSize); + params.set(Keys::kNudbBlockSize, blockSize); return params; } diff --git a/src/test/nodestore/Timing_test.cpp b/src/test/nodestore/Timing_test.cpp index 67feace198..f5e6bf8aa4 100644 --- a/src/test/nodestore/Timing_test.cpp +++ b/src/test/nodestore/Timing_test.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include @@ -12,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -661,9 +662,10 @@ public: { beast::TempDir const tempDir; Section config = parse(configString); - config.set("path", tempDir.path()); + config.set(Keys::kPath, tempDir.path()); std::stringstream ss; - ss << std::left << setw(10) << get(config, "type", std::string()) << std::right; + ss << std::left << setw(10) << get(config, Keys::kType, std::string()) + << std::right; for (auto const& test : tests) { ss << " " << setw(w) << toString(doTest(test.second, config, params, journal)); diff --git a/src/test/overlay/cluster_test.cpp b/src/test/overlay/cluster_test.cpp index 0c164dfded..6c2114b7de 100644 --- a/src/test/overlay/cluster_test.cpp +++ b/src/test/overlay/cluster_test.cpp @@ -3,9 +3,9 @@ #include -#include #include #include +#include #include #include #include diff --git a/src/test/rpc/AmendmentBlocked_test.cpp b/src/test/rpc/AmendmentBlocked_test.cpp index ae4fb99542..850d6db35b 100644 --- a/src/test/rpc/AmendmentBlocked_test.cpp +++ b/src/test/rpc/AmendmentBlocked_test.cpp @@ -9,10 +9,10 @@ #include #include -#include #include #include +#include #include #include #include @@ -20,6 +20,7 @@ #include #include +#include namespace xrpl { @@ -30,7 +31,7 @@ class AmendmentBlocked_test : public beast::unit_test::Suite { using namespace test::jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue"); + cfg->loadFromString(std::string("[") + Sections::kSigningSupport + "]\ntrue"); return cfg; })}; auto const gw = Account{"gateway"}; diff --git a/src/test/rpc/Feature_test.cpp b/src/test/rpc/Feature_test.cpp index d899b6dfd9..8cda5965cb 100644 --- a/src/test/rpc/Feature_test.cpp +++ b/src/test/rpc/Feature_test.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -276,8 +277,8 @@ class Feature_test : public beast::unit_test::Suite using namespace test::jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - (*cfg)["port_rpc"].set("admin", ""); - (*cfg)["port_ws"].set("admin", ""); + (*cfg)[Sections::kPortRpc].set(Keys::kAdmin, ""); + (*cfg)[Sections::kPortWs].set(Keys::kAdmin, ""); return cfg; })}; diff --git a/src/test/rpc/JSONRPC_test.cpp b/src/test/rpc/JSONRPC_test.cpp index 1f24d229f2..7f6b123533 100644 --- a/src/test/rpc/JSONRPC_test.cpp +++ b/src/test/rpc/JSONRPC_test.cpp @@ -12,12 +12,12 @@ #include #include -#include #include #include #include #include +#include #include #include #include @@ -2375,8 +2375,9 @@ public: testcase("autofill escalated fees"); using namespace test::jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue"); - cfg->section("transaction_queue").set("minimum_txn_in_ledger_standalone", "3"); + cfg->loadFromString(std::string("[") + Sections::kSigningSupport + "]\ntrue"); + cfg->section(Sections::kTransactionQueue) + .set(Keys::kMinimumTxnInLedgerStandalone, "3"); return cfg; })}; LoadFeeTrack const& feeTrackOuter = env.app().getFeeTrack(); diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index f35dddffb1..af56a9e9ba 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -431,9 +432,9 @@ class LedgerRPC_test : public beast::unit_test::Suite testcase("Ledger with Queued Transactions"); using namespace test::jtx; auto cfg = envconfig([](std::unique_ptr cfg) { - auto& section = cfg->section("transaction_queue"); - section.set("minimum_txn_in_ledger_standalone", "3"); - section.set("normal_consensus_increase_percent", "0"); + auto& section = cfg->section(Sections::kTransactionQueue); + section.set(Keys::kMinimumTxnInLedgerStandalone, "3"); + section.set(Keys::kNormalConsensusIncreasePercent, "0"); return cfg; }); diff --git a/src/test/rpc/ManifestRPC_test.cpp b/src/test/rpc/ManifestRPC_test.cpp index c29f3d4e39..7e89cd029e 100644 --- a/src/test/rpc/ManifestRPC_test.cpp +++ b/src/test/rpc/ManifestRPC_test.cpp @@ -4,9 +4,9 @@ #include #include -#include #include +#include #include #include @@ -48,7 +48,7 @@ public: using namespace jtx; std::string const key = "n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7"; Env env{*this, envconfig([&key](std::unique_ptr cfg) { - cfg->section(SECTION_VALIDATORS).append(key); + cfg->section(Sections::kValidators).append(key); return cfg; })}; { diff --git a/src/test/rpc/RPCOverload_test.cpp b/src/test/rpc/RPCOverload_test.cpp index b1030ebea5..33bc84eab0 100644 --- a/src/test/rpc/RPCOverload_test.cpp +++ b/src/test/rpc/RPCOverload_test.cpp @@ -8,14 +8,15 @@ #include #include -#include #include +#include #include #include #include #include +#include #include namespace xrpl::test { @@ -29,7 +30,7 @@ public: testcase << "Overload " << (useWS ? "WS" : "HTTP") << " RPC client"; using namespace jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue"); + cfg->loadFromString(std::string("[") + Sections::kSigningSupport + "]\ntrue"); return noAdmin(std::move(cfg)); })}; diff --git a/src/test/rpc/ServerInfo_test.cpp b/src/test/rpc/ServerInfo_test.cpp index 1d52f3b83e..52a1e6cdb0 100644 --- a/src/test/rpc/ServerInfo_test.cpp +++ b/src/test/rpc/ServerInfo_test.cpp @@ -3,9 +3,9 @@ #include #include -#include #include +#include #include #include @@ -108,9 +108,9 @@ admin = 127.0.0.1 Env env(*this, makeValidatorConfig()); auto const& config = env.app().config(); - auto const rpcPort = config["port_rpc"].get("port"); - auto const grpcPort = config[SECTION_PORT_GRPC].get("port"); - auto const wsPort = config["port_ws"].get("port"); + auto const rpcPort = config[Sections::kPortRpc].get(Keys::kPort); + auto const grpcPort = config[Sections::kPortGrpc].get(Keys::kPort); + auto const wsPort = config[Sections::kPortWs].get(Keys::kPort); BEAST_EXPECT(grpcPort); BEAST_EXPECT(rpcPort); BEAST_EXPECT(wsPort); diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 9206bf42ac..5ea79c3996 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -429,7 +430,7 @@ class Simulate_test : public beast::unit_test::Suite using namespace jtx; Env env(*this, envconfig([](std::unique_ptr cfg) { - cfg->section("transaction_queue").set("minimum_txn_in_ledger_standalone", "3"); + cfg->section(Sections::kTransactionQueue).set(Keys::kMinimumTxnInLedgerStandalone, "3"); return cfg; })); diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index 090a599e7c..5c519f3869 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -18,12 +18,12 @@ #include #include -#include #include #include #include #include +#include #include #include #include @@ -431,9 +431,10 @@ public: Env env{*this, singleThreadIo(envconfig(validator, "")), features}; auto& cfg = env.app().config(); - if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty())) + if (!BEAST_EXPECT(cfg.section(Sections::kValidationSeed).empty())) return; - auto const parsedseed = parseBase58(cfg.section(SECTION_VALIDATION_SEED).values()[0]); + auto const parsedseed = + parseBase58(cfg.section(Sections::kValidationSeed).values()[0]); if (BEAST_EXPECT(parsedseed); not parsedseed.has_value()) return; diff --git a/src/test/rpc/ValidatorInfo_test.cpp b/src/test/rpc/ValidatorInfo_test.cpp index 8acdca8a1f..ece0aa1224 100644 --- a/src/test/rpc/ValidatorInfo_test.cpp +++ b/src/test/rpc/ValidatorInfo_test.cpp @@ -4,9 +4,9 @@ #include #include -#include #include +#include #include #include @@ -68,7 +68,7 @@ public: "5AqDedFv5TJa2w0i21eq3MYywLVJZnFOr7C0kw2AiTzSCjIzditQ8="; Env env{*this, envconfig([&tokenBlob](std::unique_ptr cfg) { - cfg->section(SECTION_VALIDATOR_TOKEN).append(tokenBlob); + cfg->section(Sections::kValidatorToken).append(tokenBlob); return cfg; })}; { diff --git a/src/test/rpc/ValidatorRPC_test.cpp b/src/test/rpc/ValidatorRPC_test.cpp index 1c6fb94fac..360cf13c75 100644 --- a/src/test/rpc/ValidatorRPC_test.cpp +++ b/src/test/rpc/ValidatorRPC_test.cpp @@ -5,12 +5,12 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -90,7 +90,7 @@ public: *this, envconfig([&keys](std::unique_ptr cfg) { for (auto const& key : keys) - cfg->section(SECTION_VALIDATORS).append(key); + cfg->section(Sections::kValidators).append(key); return cfg; }), }; @@ -200,8 +200,8 @@ public: Env env{ *this, envconfig([&](std::unique_ptr cfg) { - cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI); - cfg->section(SECTION_VALIDATOR_LIST_KEYS) + cfg->section(Sections::kValidatorListSites).append(siteURI); + cfg->section(Sections::kValidatorListKeys) .append(strHex(server->publisherPublic())); return cfg; }), @@ -260,8 +260,8 @@ public: Env env{ *this, envconfig([&](std::unique_ptr cfg) { - cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI); - cfg->section(SECTION_VALIDATOR_LIST_KEYS) + cfg->section(Sections::kValidatorListSites).append(siteURI); + cfg->section(Sections::kValidatorListKeys) .append(strHex(server->publisherPublic())); return cfg; }), @@ -323,8 +323,8 @@ public: Env env{ *this, envconfig([&](std::unique_ptr cfg) { - cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI); - cfg->section(SECTION_VALIDATOR_LIST_KEYS) + cfg->section(Sections::kValidatorListSites).append(siteURI); + cfg->section(Sections::kValidatorListKeys) .append(strHex(server->publisherPublic())); return cfg; }), @@ -416,8 +416,8 @@ public: Env env{ *this, envconfig([&](std::unique_ptr cfg) { - cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI); - cfg->section(SECTION_VALIDATOR_LIST_KEYS) + cfg->section(Sections::kValidatorListSites).append(siteURI); + cfg->section(Sections::kValidatorListKeys) .append(strHex(server->publisherPublic())); return cfg; }), diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index 6569a2d2a4..5ba71962b3 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -55,22 +56,23 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En static auto makeConfig(std::string const& proto, bool admin = true, bool credentials = false) { - auto const sectionName = boost::starts_with(proto, "h") ? "port_rpc" : "port_ws"; + auto const sectionName = + boost::starts_with(proto, "h") ? Sections::kPortRpc : Sections::kPortWs; auto p = jtx::envconfig(); - p->overwrite(sectionName, "protocol", proto); + p->overwrite(sectionName, Keys::kProtocol, proto); if (!admin) - p->overwrite(sectionName, "admin", ""); + p->overwrite(sectionName, Keys::kAdmin, ""); if (credentials) { - (*p)[sectionName].set("admin_password", "p"); - (*p)[sectionName].set("admin_user", "u"); + (*p)[sectionName].set(Keys::kAdminPassword, "p"); + (*p)[sectionName].set(Keys::kAdminUser, "u"); } p->overwrite( - boost::starts_with(proto, "h") ? "port_ws" : "port_rpc", - "protocol", + boost::starts_with(proto, "h") ? Sections::kPortWs : Sections::kPortRpc, + Keys::kProtocol, boost::starts_with(proto, "h") ? "ws" : "http"); if (proto == "https") @@ -78,11 +80,11 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En // this port is here to allow the env to create its internal client, // which requires an http endpoint to talk to. In the connection // failure test, this endpoint should never be used - (*p)["server"].append("port_alt"); - (*p)["port_alt"].set("ip", getEnvLocalhostAddr()); - (*p)["port_alt"].set("port", "7099"); - (*p)["port_alt"].set("protocol", "http"); - (*p)["port_alt"].set("admin", getEnvLocalhostAddr()); + (*p)[Sections::kServer].append("port_alt"); + (*p)["port_alt"].set(Keys::kIp, getEnvLocalhostAddr()); + (*p)["port_alt"].set(Keys::kPort, "7099"); + (*p)["port_alt"].set(Keys::kProtocol, "http"); + (*p)["port_alt"].set(Keys::kAdmin, getEnvLocalhostAddr()); } return p; @@ -212,8 +214,8 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En boost::beast::http::response& resp, boost::system::error_code& ec) { - auto const port = env.app().config()["port_ws"].get("port"); - auto ip = env.app().config()["port_ws"].get("ip"); + auto const port = env.app().config()[Sections::kPortWs].get(Keys::kPort); + auto ip = env.app().config()[Sections::kPortWs].get(Keys::kIp); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) doRequest(yield, makeWSUpgrade(*ip, *port), *ip, *port, secure, resp, ec); return; @@ -229,8 +231,8 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En std::string const& body = "", MyFields const& fields = {}) { - auto const port = env.app().config()["port_rpc"].get("port"); - auto const ip = env.app().config()["port_rpc"].get("ip"); + auto const port = env.app().config()[Sections::kPortRpc].get(Keys::kPort); + auto const ip = env.app().config()[Sections::kPortRpc].get(Keys::kIp); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) doRequest(yield, makeHTTPRequest(*ip, *port, body, fields), *ip, *port, secure, resp, ec); return; @@ -298,12 +300,13 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En if (admin && credentials) { - auto const user = - env.app().config()[protoWs ? "port_ws" : "port_rpc"].get("admin_user"); + auto const user = env.app() + .config()[protoWs ? Sections::kPortWs : Sections::kPortRpc] + .get(Keys::kAdminUser); - auto const password = - env.app().config()[protoWs ? "port_ws" : "port_rpc"].get( - "admin_password"); + auto const password = env.app() + .config()[protoWs ? Sections::kPortWs : Sections::kPortRpc] + .get(Keys::kAdminPassword); // 1 - FAILS with wrong pass // NOLINTNEXTLINE(bugprone-unchecked-optional-access) @@ -368,7 +371,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En testcase("WS client to http server fails"); using namespace jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->section("port_ws").set("protocol", "http,https"); + cfg->section(Sections::kPortWs).set(Keys::kProtocol, "http,https"); return cfg; })}; @@ -399,8 +402,8 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En testcase("Status request"); using namespace jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->section("port_rpc").set("protocol", "ws2,wss2"); - cfg->section("port_ws").set("protocol", "http"); + cfg->section(Sections::kPortRpc).set(Keys::kProtocol, "ws2,wss2"); + cfg->section(Sections::kPortWs).set(Keys::kProtocol, "http"); return cfg; })}; @@ -433,12 +436,12 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En using namespace boost::asio; using namespace boost::beast::http; Env env{*this, envconfig([](std::unique_ptr cfg) { - cfg->section("port_ws").set("protocol", "ws2"); + cfg->section(Sections::kPortWs).set(Keys::kProtocol, "ws2"); return cfg; })}; - auto const port = env.app().config()["port_ws"].get("port"); - auto const ip = env.app().config()["port_ws"].get("ip"); + auto const port = env.app().config()[Sections::kPortWs].get(Keys::kPort); + auto const ip = env.app().config()[Sections::kPortWs].get(Keys::kIp); boost::system::error_code ec; response resp; @@ -505,11 +508,11 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En using namespace test::jtx; Env env{*this, envconfig([secure](std::unique_ptr cfg) { - (*cfg)["port_rpc"].set("user", "me"); - (*cfg)["port_rpc"].set("password", "secret"); - (*cfg)["port_rpc"].set("protocol", secure ? "https" : "http"); + (*cfg)[Sections::kPortRpc].set(Keys::kUser, "me"); + (*cfg)[Sections::kPortRpc].set(Keys::kPassword, "secret"); + (*cfg)[Sections::kPortRpc].set(Keys::kProtocol, secure ? "https" : "http"); if (secure) - (*cfg)["port_ws"].set("protocol", "http,ws"); + (*cfg)[Sections::kPortWs].set(Keys::kProtocol, "http,ws"); return cfg; })}; @@ -533,11 +536,11 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En doHTTPRequest(env, yield, secure, resp, ec, to_string(jr), auth); BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const user = env.app().config().section("port_rpc").get("user").value(); - auto const pass = - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - env.app().config().section("port_rpc").get("password").value(); + auto const section = env.app().config().section(Sections::kPortRpc); + // NOLINTBEGIN(bugprone-unchecked-optional-access) + auto const user = section.get(Keys::kUser).value(); + auto const pass = section.get(Keys::kPassword).value(); + // NOLINTEND(bugprone-unchecked-optional-access) // try with the correct user/pass, but not encoded auth.set("Authorization", "Basic " + user + ":" + pass); @@ -560,15 +563,15 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En using namespace boost::asio; using namespace boost::beast::http; Env env{*this, envconfig([&](std::unique_ptr cfg) { - (*cfg)["port_rpc"].set("limit", std::to_string(limit)); + (*cfg)[Sections::kPortRpc].set(Keys::kLimit, std::to_string(limit)); return cfg; })}; - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const port = env.app().config()["port_rpc"].get("port").value(); - - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const ip = env.app().config()["port_rpc"].get("ip").value(); + auto const section = env.app().config().section(Sections::kPortRpc); + // NOLINTBEGIN(bugprone-unchecked-optional-access) + auto const port = section.get(Keys::kPort).value(); + auto const ip = section.get(Keys::kIp).value(); + // NOLINTEND(bugprone-unchecked-optional-access) boost::system::error_code ec; io_context& ios = getIoContext(); @@ -620,14 +623,15 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En using namespace test::jtx; Env env{*this, envconfig([](std::unique_ptr cfg) { - (*cfg)["port_ws"].set("protocol", "wss"); + (*cfg)[Sections::kPortWs].set(Keys::kProtocol, "wss"); return cfg; })}; - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const port = env.app().config()["port_ws"].get("port").value(); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const ip = env.app().config()["port_ws"].get("ip").value(); + auto const section = env.app().config().section(Sections::kPortWs); + // NOLINTBEGIN(bugprone-unchecked-optional-access) + auto const port = section.get(Keys::kPort).value(); + auto const ip = section.get(Keys::kIp).value(); + // NOLINTEND(bugprone-unchecked-optional-access) boost::beast::http::response resp; boost::system::error_code ec; doRequest(yield, makeWSUpgrade(ip, port), ip, port, true, resp, ec); @@ -644,10 +648,11 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En using namespace test::jtx; Env env{*this}; - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const port = env.app().config()["port_ws"].get("port").value(); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const ip = env.app().config()["port_ws"].get("ip").value(); + auto const section = env.app().config().section(Sections::kPortWs); + // NOLINTBEGIN(bugprone-unchecked-optional-access) + auto const port = section.get(Keys::kPort).value(); + auto const ip = section.get(Keys::kIp).value(); + // NOLINTEND(bugprone-unchecked-optional-access) boost::beast::http::response resp; boost::system::error_code ec; // body content is required here to avoid being @@ -667,10 +672,11 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En using namespace boost::beast::http; Env env{*this}; - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const port = env.app().config()["port_ws"].get("port").value(); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const ip = env.app().config()["port_ws"].get("ip").value(); + auto const section = env.app().config().section(Sections::kPortWs); + // NOLINTBEGIN(bugprone-unchecked-optional-access) + auto const port = section.get(Keys::kPort).value(); + auto const ip = section.get(Keys::kIp).value(); + // NOLINTEND(bugprone-unchecked-optional-access) boost::system::error_code ec; io_context& ios = getIoContext(); @@ -746,7 +752,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En *this, validator( envconfig([](std::unique_ptr cfg) { - cfg->section("port_rpc").set("protocol", "http"); + cfg->section(Sections::kPortRpc).set(Keys::kProtocol, "http"); return cfg; }), "")}; @@ -774,8 +780,8 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En BEAST_EXPECT(env.app().getOPs().getConsensusInfo()["validating"] == true); BEAST_EXPECT(!si[jss::state].isMember(jss::warnings)); - auto const portWs = env.app().config()["port_ws"].get("port"); - auto const ipWs = env.app().config()["port_ws"].get("ip"); + auto const portWs = env.app().config()[Sections::kPortWs].get(Keys::kPort); + auto const ipWs = env.app().config()[Sections::kPortWs].get(Keys::kIp); boost::system::error_code ec; response resp; @@ -874,7 +880,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En *this, validator( envconfig([](std::unique_ptr cfg) { - cfg->section("port_rpc").set("protocol", "http"); + cfg->section(Sections::kPortRpc).set(Keys::kProtocol, "http"); return cfg; }), "")}; @@ -902,8 +908,8 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En BEAST_EXPECT(env.app().getOPs().getConsensusInfo()["validating"] == true); BEAST_EXPECT(!si[jss::state].isMember(jss::warnings)); - auto const portWs = env.app().config()["port_ws"].get("port"); - auto const ipWs = env.app().config()["port_ws"].get("ip"); + auto const portWs = env.app().config()[Sections::kPortWs].get(Keys::kPort); + auto const ipWs = env.app().config()[Sections::kPortWs].get(Keys::kIp); boost::system::error_code ec; response resp; diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp index 1b0107167c..455a365b7c 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -4,11 +4,11 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -396,7 +396,7 @@ public: Env const env{ *this, envconfig([](std::unique_ptr cfg) { - (*cfg).deprecatedClearSection("port_rpc"); + (*cfg).deprecatedClearSection(Sections::kPortRpc); return cfg; }), std::make_unique(&messages)}; @@ -407,8 +407,8 @@ public: Env const env{ *this, envconfig([](std::unique_ptr cfg) { - (*cfg).deprecatedClearSection("port_rpc"); - (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr()); + (*cfg).deprecatedClearSection(Sections::kPortRpc); + (*cfg)[Sections::kPortRpc].set(Keys::kIp, getEnvLocalhostAddr()); return cfg; }), std::make_unique(&messages)}; @@ -419,9 +419,9 @@ public: Env const env{ *this, envconfig([](std::unique_ptr cfg) { - (*cfg).deprecatedClearSection("port_rpc"); - (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr()); - (*cfg)["port_rpc"].set("port", "0"); + (*cfg).deprecatedClearSection(Sections::kPortRpc); + (*cfg)[Sections::kPortRpc].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortRpc].set(Keys::kPort, "0"); return cfg; }), std::make_unique(&messages)}; @@ -433,7 +433,7 @@ public: Env const env{ *this, envconfig([](std::unique_ptr cfg) { - (*cfg)["server"].set("port", "0"); + (*cfg)[Sections::kServer].set(Keys::kPort, "0"); return cfg; }), std::make_unique(&messages)}; @@ -445,10 +445,10 @@ public: Env const env{ *this, envconfig([](std::unique_ptr cfg) { - (*cfg).deprecatedClearSection("port_rpc"); - (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr()); - (*cfg)["port_rpc"].set("port", "8081"); - (*cfg)["port_rpc"].set("protocol", ""); + (*cfg).deprecatedClearSection(Sections::kPortRpc); + (*cfg)[Sections::kPortRpc].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortRpc].set(Keys::kPort, "8081"); + (*cfg)[Sections::kPortRpc].set(Keys::kProtocol, ""); return cfg; }), std::make_unique(&messages)}; @@ -462,22 +462,22 @@ public: *this, envconfig([](std::unique_ptr cfg) { cfg = std::make_unique(); - cfg->overwrite(ConfigSection::nodeDatabase(), "type", "memory"); - cfg->overwrite(ConfigSection::nodeDatabase(), "path", "main"); - cfg->deprecatedClearSection(ConfigSection::importNodeDatabase()); - cfg->legacy("database_path", ""); + cfg->overwrite(Sections::kNodeDatabase, Keys::kType, "memory"); + cfg->overwrite(Sections::kNodeDatabase, Keys::kPath, "main"); + cfg->deprecatedClearSection(Sections::kImportNodeDatabase); + cfg->legacy(Sections::kDatabasePath, ""); cfg->setupControl(true, true, true); - (*cfg)["port_peer"].set("ip", getEnvLocalhostAddr()); - (*cfg)["port_peer"].set("port", "8080"); - (*cfg)["port_peer"].set("protocol", "peer"); - (*cfg)["port_rpc"].set("ip", getEnvLocalhostAddr()); - (*cfg)["port_rpc"].set("port", "8081"); - (*cfg)["port_rpc"].set("protocol", "http,ws2"); - (*cfg)["port_rpc"].set("admin", getEnvLocalhostAddr()); - (*cfg)["port_ws"].set("ip", getEnvLocalhostAddr()); - (*cfg)["port_ws"].set("port", "8082"); - (*cfg)["port_ws"].set("protocol", "ws"); - (*cfg)["port_ws"].set("admin", getEnvLocalhostAddr()); + (*cfg)[Sections::kPortPeer].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortPeer].set(Keys::kPort, "8080"); + (*cfg)[Sections::kPortPeer].set(Keys::kProtocol, "peer"); + (*cfg)[Sections::kPortRpc].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortRpc].set(Keys::kPort, "8081"); + (*cfg)[Sections::kPortRpc].set(Keys::kProtocol, "http,ws2"); + (*cfg)[Sections::kPortRpc].set(Keys::kAdmin, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortWs].set(Keys::kIp, getEnvLocalhostAddr()); + (*cfg)[Sections::kPortWs].set(Keys::kPort, "8082"); + (*cfg)[Sections::kPortWs].set(Keys::kProtocol, "ws"); + (*cfg)[Sections::kPortWs].set(Keys::kAdmin, getEnvLocalhostAddr()); return cfg; }), std::make_unique(&messages)}; @@ -491,14 +491,14 @@ public: *this, envconfig([](std::unique_ptr cfg) { cfg = std::make_unique(); - cfg->overwrite(ConfigSection::nodeDatabase(), "type", "memory"); - cfg->overwrite(ConfigSection::nodeDatabase(), "path", "main"); - cfg->deprecatedClearSection(ConfigSection::importNodeDatabase()); - cfg->legacy("database_path", ""); + cfg->overwrite(Sections::kNodeDatabase, Keys::kType, "memory"); + cfg->overwrite(Sections::kNodeDatabase, Keys::kPath, "main"); + cfg->deprecatedClearSection(Sections::kImportNodeDatabase); + cfg->legacy(Sections::kDatabasePath, ""); cfg->setupControl(true, true, true); - (*cfg)["server"].append("port_peer"); - (*cfg)["server"].append("port_rpc"); - (*cfg)["server"].append("port_ws"); + (*cfg)[Sections::kServer].append(Sections::kPortPeer); + (*cfg)[Sections::kServer].append(Sections::kPortRpc); + (*cfg)[Sections::kServer].append(Sections::kPortWs); return cfg; }), std::make_unique(&messages)}; diff --git a/src/test/shamap/common.h b/src/test/shamap/common.h index 49f1c07741..0475acdf6d 100644 --- a/src/test/shamap/common.h +++ b/src/test/shamap/common.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include #include @@ -33,8 +35,8 @@ public: , j_(j) { Section testSection; - testSection.set("type", "memory"); - testSection.set("path", "SHAMap_test"); + testSection.set(Keys::kType, "memory"); + testSection.set(Keys::kPath, "SHAMap_test"); db_ = NodeStore::Manager::instance().makeDatabase( megabytes(4), scheduler_, 1, testSection, j); } diff --git a/src/tests/libxrpl/helpers/TestFamily.h b/src/tests/libxrpl/helpers/TestFamily.h index dea7a6d4b4..50f514480a 100644 --- a/src/tests/libxrpl/helpers/TestFamily.h +++ b/src/tests/libxrpl/helpers/TestFamily.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include #include @@ -37,8 +39,8 @@ public: , j_(j) { Section config; - config.set("type", "memory"); - config.set("path", "TestFamily"); + config.set(Keys::kType, "memory"); + config.set(Keys::kPath, "TestFamily"); db_ = NodeStore::Manager::instance().makeDatabase(megabytes(4), scheduler_, 1, config, j); } diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index af5d51289d..67b5e30eb7 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -40,7 +39,6 @@ #include #include -#include #include #include #include @@ -56,6 +54,8 @@ #include #include #include +#include +#include #include #include #include @@ -316,13 +316,14 @@ public: // PerfLog must be started before any other threads are launched. , perfLog_( perf::makePerfLog( - perf::setupPerfLog(config_->section("perf"), config_->configDir), + perf::setupPerfLog(config_->section(Sections::kPerf), config_->configDir), *this, logs_->journal("PerfLog"), [this] { signalStop("PerfLog"); })) , txMaster_(*this) - , collectorManager_( - makeCollectorManager(config_->section(SECTION_INSIGHT), logs_->journal("Collector"))) + , collectorManager_(makeCollectorManager( + config_->section(Sections::kInsight), + logs_->journal("Collector"))) , jobQueue_( std::make_unique( [](std::unique_ptr const& config) { @@ -864,7 +865,7 @@ public: megabytes(config_->getValueFor(SizedItem::BurstSize, std::nullopt)), dummyScheduler, 0, - config_->section(ConfigSection::importNodeDatabase()), + config_->section(Sections::kImportNodeDatabase), j); JLOG(j.warn()) << "Starting node import from '" << source->getName() << "' to '" @@ -1224,9 +1225,9 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) } return supported; }(); - Section const& downVoted = config_->section(SECTION_VETO_AMENDMENTS); + Section const& downVoted = config_->section(Sections::kVetoAmendments); - Section const& upVoted = config_->section(SECTION_AMENDMENTS); + Section const& upVoted = config_->section(Sections::kAmendments); amendmentTable_ = makeAmendmentTable( *this, @@ -1295,7 +1296,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) nodeIdentity_ = getNodeIdentity(*this, cmdline); - if (!cluster_->load(config().section(SECTION_CLUSTER_NODES))) + if (!cluster_->load(config().section(Sections::kClusterNodes))) { JLOG(journal_.fatal()) << "Invalid entry in cluster configuration."; return false; @@ -1309,7 +1310,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) getWalletDB(), "ValidatorManifests", validatorKeys_.manifest, - config().section(SECTION_VALIDATOR_KEY_REVOCATION).values())) + config().section(Sections::kValidatorKeyRevocation).values())) { JLOG(journal_.fatal()) << "Invalid configured validator manifest."; return false; @@ -1320,7 +1321,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) // It is possible to have a valid ValidatorKeys object without // setting the signingKey or masterKey. This occurs if the // configuration file does not have either - // SECTION_VALIDATOR_TOKEN or SECTION_VALIDATION_SEED section. + // Sections::kValidatorToken or Sections::kValidationSeed section. // masterKey for the configuration-file specified validator keys std::optional localSigningKey; @@ -1330,8 +1331,8 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) // Setup trusted validators if (!validators_->load( localSigningKey, - config().section(SECTION_VALIDATORS).values(), - config().section(SECTION_VALIDATOR_LIST_KEYS).values(), + config().section(Sections::kValidators).values(), + config().section(Sections::kValidatorListKeys).values(), config().validatorListThreshold)) { JLOG(journal_.fatal()) << "Invalid entry in validator configuration."; @@ -1339,9 +1340,9 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) } } - if (!validatorSites_->load(config().section(SECTION_VALIDATOR_LIST_SITES).values())) + if (!validatorSites_->load(config().section(Sections::kValidatorListSites).values())) { - JLOG(journal_.fatal()) << "Invalid entry in [" << SECTION_VALIDATOR_LIST_SITES << "]"; + JLOG(journal_.fatal()) << "Invalid entry in [" << Sections::kValidatorListSites << "]"; return false; } @@ -1439,7 +1440,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) // // Execute start up rpc commands. // - for (auto const& cmd : config_->section(SECTION_RPC_STARTUP).lines()) + for (auto const& cmd : config_->section(Sections::kRpcStartup).lines()) { json::Reader jrReader; json::Value jvCommand; @@ -1447,7 +1448,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) if (!jrReader.parse(cmd, jvCommand)) { JLOG(journal_.fatal()) - << "Couldn't parse entry in [" << SECTION_RPC_STARTUP << "]: '" << cmd; + << "Couldn't parse entry in [" << Sections::kRpcStartup << "]: '" << cmd; } if (!config_->quiet()) @@ -1503,7 +1504,7 @@ ApplicationImp::start(bool withTimers) overlay_->start(); if (grpcServer_->start()) - fixConfigPorts(*config_, {{SECTION_PORT_GRPC, grpcServer_->getEndpoint()}}); + fixConfigPorts(*config_, {{Sections::kPortGrpc, grpcServer_->getEndpoint()}}); ledgerCleaner_->start(); perfLog_->start(); @@ -2170,12 +2171,12 @@ fixConfigPorts(Config& config, Endpoints const& endpoints) continue; auto& section = config[name]; - auto const optPort = section.get("port"); + auto const optPort = section.get(Keys::kPort); if (optPort) { std::uint16_t const port = beast::lexicalCast(*optPort); if (port == 0u) - section.set("port", std::to_string(ep.port())); + section.set(Keys::kPort, std::to_string(ep.port())); } } } diff --git a/src/xrpld/app/main/CollectorManager.cpp b/src/xrpld/app/main/CollectorManager.cpp index 6cdbca8d8a..9e1278607f 100644 --- a/src/xrpld/app/main/CollectorManager.cpp +++ b/src/xrpld/app/main/CollectorManager.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -8,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -25,13 +26,13 @@ public: CollectorManagerImp(Section const& params, beast::Journal journal) : journal_(journal) { - std::string const& server = get(params, "server"); + std::string const& server = get(params, Keys::kServer); if (server == "statsd") { beast::IP::Endpoint const address( - beast::IP::Endpoint::fromString(get(params, "address"))); - std::string const& prefix(get(params, "prefix")); + beast::IP::Endpoint::fromString(get(params, Keys::kAddress))); + std::string const& prefix(get(params, Keys::kPrefix)); collector_ = beast::insight::StatsDCollector::make(address, prefix, journal); } diff --git a/src/xrpld/app/main/CollectorManager.h b/src/xrpld/app/main/CollectorManager.h index e736ae57db..d07da15353 100644 --- a/src/xrpld/app/main/CollectorManager.h +++ b/src/xrpld/app/main/CollectorManager.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include namespace xrpl { diff --git a/src/xrpld/app/main/GRPCServer.cpp b/src/xrpld/app/main/GRPCServer.cpp index 7a622f632f..1af1343fc5 100644 --- a/src/xrpld/app/main/GRPCServer.cpp +++ b/src/xrpld/app/main/GRPCServer.cpp @@ -1,13 +1,11 @@ #include #include -#include #include #include #include #include -#include #include #include #include @@ -15,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -335,15 +335,15 @@ GRPCServerImpl::GRPCServerImpl(Application& app) : app_(app), journal_(app_.getJournal("gRPC Server")) { // if present, get endpoint from config - if (app_.config().exists(SECTION_PORT_GRPC)) + if (app_.config().exists(Sections::kPortGrpc)) { - Section const& section = app_.config().section(SECTION_PORT_GRPC); + Section const& section = app_.config().section(Sections::kPortGrpc); - auto const optIp = section.get("ip"); + auto const optIp = section.get(Keys::kIp); if (!optIp) return; - auto const optPort = section.get("port"); + auto const optPort = section.get(Keys::kPort); if (!optPort) return; try @@ -361,7 +361,7 @@ GRPCServerImpl::GRPCServerImpl(Application& app) Throw("Error setting grpc server address"); } - auto const optSecureGateway = section.get("secure_gateway"); + auto const optSecureGateway = section.get(Keys::kSecureGateway); if (optSecureGateway) { try @@ -391,10 +391,10 @@ GRPCServerImpl::GRPCServerImpl(Application& app) } // Read TLS certificate configuration (optional) - sslCertPath_ = section.get("ssl_cert"); - sslKeyPath_ = section.get("ssl_key"); - sslCertChainPath_ = section.get("ssl_cert_chain"); - sslClientCAPath_ = section.get("ssl_client_ca"); + sslCertPath_ = section.get(Keys::kSslCert); + sslKeyPath_ = section.get(Keys::kSslKey); + sslCertChainPath_ = section.get(Keys::kSslCertChain); + sslClientCAPath_ = section.get(Keys::kSslClientCa); // If cert or key is specified, both must be specified if (sslCertPath_.has_value() || sslKeyPath_.has_value()) diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index f470a2d80f..d0b40efce8 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -362,10 +362,10 @@ run(int argc, char** argv) std::string importText; { importText += "Import an existing node database (specified in the ["; - importText += ConfigSection::importNodeDatabase(); + importText += Sections::kImportNodeDatabase; importText += "] configuration file section) into the current "; importText += "node database (specified in the ["; - importText += ConfigSection::nodeDatabase(); + importText += Sections::kNodeDatabase; importText += "] configuration file section)."; } diff --git a/src/xrpld/app/main/NodeIdentity.cpp b/src/xrpld/app/main/NodeIdentity.cpp index 656e2b91a2..8198c43af7 100644 --- a/src/xrpld/app/main/NodeIdentity.cpp +++ b/src/xrpld/app/main/NodeIdentity.cpp @@ -2,9 +2,9 @@ #include #include -#include #include +#include #include #include #include @@ -31,12 +31,15 @@ getNodeIdentity(Application& app, boost::program_options::variables_map const& c if (!seed) Throw("Invalid 'nodeid' in command line"); } - else if (app.config().exists(SECTION_NODE_SEED)) + else if (app.config().exists(Sections::kNodeSeed)) { - seed = parseBase58(app.config().section(SECTION_NODE_SEED).lines().front()); + seed = parseBase58(app.config().section(Sections::kNodeSeed).lines().front()); if (!seed) - Throw("Invalid [" SECTION_NODE_SEED "] in configuration file"); + { + Throw( + std::string("Invalid [") + Sections::kNodeSeed + "] in configuration file"); + } } if (seed) diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index 54cf85ba35..917cf6aeb2 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -54,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -313,7 +313,7 @@ public: , consensus_( registry_.get().getApp(), makeFeeVote( - setupFeeVote(registry_.get().getApp().config().section("voting")), + setupFeeVote(registry_.get().getApp().config().section(Sections::kVoting)), registry_.get().getJournal("FeeVote")), ledgerMaster, *localTX_, @@ -2973,11 +2973,12 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) } } - if (registry_.get().getApp().config().exists(SECTION_PORT_GRPC)) + if (registry_.get().getApp().config().exists(Sections::kPortGrpc)) { - auto const& grpcSection = registry_.get().getApp().config().section(SECTION_PORT_GRPC); - auto const optPort = grpcSection.get("port"); - if (optPort && grpcSection.get("ip")) + auto const& grpcSection = + registry_.get().getApp().config().section(Sections::kPortGrpc); + auto const optPort = grpcSection.get(Keys::kPort); + if (optPort && grpcSection.get(Keys::kIp)) { auto& jv = ports.append(json::Value(json::ValueType::Object)); jv[jss::port] = *optPort; diff --git a/src/xrpld/app/misc/SHAMapStoreImp.cpp b/src/xrpld/app/misc/SHAMapStoreImp.cpp index 3f4bd6280f..7259c233e4 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.cpp +++ b/src/xrpld/app/misc/SHAMapStoreImp.cpp @@ -4,15 +4,15 @@ #include #include #include -#include -#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -101,44 +101,45 @@ SHAMapStoreImp::SHAMapStoreImp( { Config& config{app.config()}; - Section& section{config.section(ConfigSection::nodeDatabase())}; + Section& section{config.section(Sections::kNodeDatabase)}; if (section.empty()) { Throw( - "Missing [" + ConfigSection::nodeDatabase() + "] entry in configuration file"); + std::string("Missing [") + Sections::kNodeDatabase + "] entry in configuration file"); } // RocksDB only. Use sensible defaults if no values specified. - if (boost::iequals(get(section, "type"), "RocksDB")) + if (boost::iequals(get(section, Keys::kType), "RocksDB")) { - if (!section.exists("cache_mb")) + if (!section.exists(Keys::kCacheMb)) { - section.set("cache_mb", std::to_string(config.getValueFor(SizedItem::HashNodeDbCache))); + section.set( + Keys::kCacheMb, std::to_string(config.getValueFor(SizedItem::HashNodeDbCache))); } - if (!section.exists("filter_bits") && (config.nodeSize >= 2)) - section.set("filter_bits", "10"); + if (!section.exists(Keys::kFilterBits) && (config.nodeSize >= 2)) + section.set(Keys::kFilterBits, "10"); } - getIfExists(section, "online_delete", deleteInterval_); + getIfExists(section, Keys::kOnlineDelete, deleteInterval_); if (deleteInterval_ != 0u) { // Configuration that affects the behavior of online delete - getIfExists(section, "delete_batch", deleteBatch_); + getIfExists(section, Keys::kDeleteBatch, deleteBatch_); std::uint32_t temp = 0; - if (getIfExists(section, "back_off_milliseconds", temp) || + if (getIfExists(section, Keys::kBackOffMilliseconds, temp) || // Included for backward compatibility with an undocumented setting - getIfExists(section, "backOff", temp)) + getIfExists(section, Keys::kBackOff, temp)) { backOff_ = std::chrono::milliseconds{temp}; } - if (getIfExists(section, "age_threshold_seconds", temp)) + if (getIfExists(section, Keys::kAgeThresholdSeconds, temp)) ageThreshold_ = std::chrono::seconds{temp}; - if (getIfExists(section, "recovery_wait_seconds", temp)) + if (getIfExists(section, Keys::kRecoveryWaitSeconds, temp)) recoveryWaitTime_ = std::chrono::seconds{temp}; - getIfExists(section, "advisory_delete", advisoryDelete_); + getIfExists(section, Keys::kAdvisoryDelete, advisoryDelete_); auto const minInterval = config.standalone() ? kMinimumDeletionIntervalSa : kMinimumDeletionInterval; @@ -164,20 +165,20 @@ SHAMapStoreImp::SHAMapStoreImp( std::unique_ptr SHAMapStoreImp::makeNodeStore(int readThreads) { - auto nscfg = app_.config().section(ConfigSection::nodeDatabase()); + auto nscfg = app_.config().section(Sections::kNodeDatabase); // Provide default values. - if (!nscfg.exists("cache_size")) + if (!nscfg.exists(Keys::kCacheSize)) { nscfg.set( - "cache_size", + Keys::kCacheSize, std::to_string(app_.config().getValueFor(SizedItem::TreeCacheSize, std::nullopt))); } - if (!nscfg.exists("cache_age")) + if (!nscfg.exists(Keys::kCacheAge)) { nscfg.set( - "cache_age", + Keys::kCacheAge, std::to_string(app_.config().getValueFor(SizedItem::TreeCacheAge, std::nullopt))); } @@ -385,8 +386,8 @@ SHAMapStoreImp::run() void SHAMapStoreImp::dbPaths() { - Section const section{app_.config().section(ConfigSection::nodeDatabase())}; - boost::filesystem::path dbPath = get(section, "path"); + Section const section{app_.config().section(Sections::kNodeDatabase)}; + boost::filesystem::path dbPath = get(section, Keys::kPath); if (boost::filesystem::exists(dbPath)) { @@ -451,7 +452,7 @@ SHAMapStoreImp::dbPaths() (!archiveDbExists && !state.archiveDb.empty()) || (writableDbExists != archiveDbExists) || state.writableDb.empty() != state.archiveDb.empty()) { - boost::filesystem::path stateDbPathName = app_.config().legacy("database_path"); + boost::filesystem::path stateDbPathName = app_.config().legacy(Sections::kDatabasePath); stateDbPathName /= dbName_; stateDbPathName += "*"; @@ -463,7 +464,7 @@ SHAMapStoreImp::dbPaths() << "The existing data is in a corrupted state.\n" << "To resume operation, remove the files matching " << stateDbPathName.string() << " and contents of the directory " - << get(section, "path") << '\n' + << get(section, Keys::kPath) << '\n' << "Optionally, you can move those files to another\n" << "location if you wish to analyze or back up the data.\n" << "However, there is no guarantee that the data in its\n" @@ -480,7 +481,7 @@ SHAMapStoreImp::dbPaths() std::unique_ptr SHAMapStoreImp::makeBackendRotating(std::string path) { - Section section{app_.config().section(ConfigSection::nodeDatabase())}; + Section section{app_.config().section(Sections::kNodeDatabase)}; boost::filesystem::path newPath; if (!path.empty()) @@ -489,12 +490,12 @@ SHAMapStoreImp::makeBackendRotating(std::string path) } else { - boost::filesystem::path p = get(section, "path"); + boost::filesystem::path p = get(section, Keys::kPath); p /= dbPrefix_; p += ".%%%%"; newPath = boost::filesystem::unique_path(p); } - section.set("path", newPath.string()); + section.set(Keys::kPath, newPath.string()); auto backend{NodeStore::Manager::instance().makeBackend( section, diff --git a/src/xrpld/app/misc/ValidatorList.h b/src/xrpld/app/misc/ValidatorList.h index dcd7a24499..3f1823f930 100644 --- a/src/xrpld/app/misc/ValidatorList.h +++ b/src/xrpld/app/misc/ValidatorList.h @@ -233,8 +233,8 @@ class ValidatorList std::optional localPubKey_; // The below variable contains the Publisher list specified in the local - // config file under the title of SECTION_VALIDATORS or [validators]. - // This list is not associated with the masterKey of any publisher. + // config file under the title of [validators]. This list is not associated + // with the masterKey of any publisher. // Apropos PublisherListCollection fields, localPublisherList does not // have any "remaining" manifests. It is assumed to be perennially diff --git a/src/xrpld/app/misc/detail/AmendmentTable.cpp b/src/xrpld/app/misc/detail/AmendmentTable.cpp index 65771f6aa3..4f331b0781 100644 --- a/src/xrpld/app/misc/detail/AmendmentTable.cpp +++ b/src/xrpld/app/misc/detail/AmendmentTable.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -8,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index c12632875d..f98580ede4 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -3,12 +3,13 @@ #include #include -#include #include #include #include #include #include +#include +#include #include #include #include @@ -1872,16 +1873,16 @@ TxQ::Setup setupTxQ(Config const& config) { TxQ::Setup setup; - auto const& section = config.section("transaction_queue"); - set(setup.ledgersInQueue, "ledgers_in_queue", section); - set(setup.queueSizeMin, "minimum_queue_size", section); - set(setup.retrySequencePercent, "retry_sequence_percent", section); - set(setup.minimumEscalationMultiplier, "minimum_escalation_multiplier", section); - set(setup.minimumTxnInLedger, "minimum_txn_in_ledger", section); - set(setup.minimumTxnInLedgerSA, "minimum_txn_in_ledger_standalone", section); - set(setup.targetTxnInLedger, "target_txn_in_ledger", section); + auto const& section = config.section(Sections::kTransactionQueue); + set(setup.ledgersInQueue, Keys::kLedgersInQueue, section); + set(setup.queueSizeMin, Keys::kMinimumQueueSize, section); + set(setup.retrySequencePercent, Keys::kRetrySequencePercent, section); + set(setup.minimumEscalationMultiplier, Keys::kMinimumEscalationMultiplier, section); + set(setup.minimumTxnInLedger, Keys::kMinimumTxnInLedger, section); + set(setup.minimumTxnInLedgerSA, Keys::kMinimumTxnInLedgerStandalone, section); + set(setup.targetTxnInLedger, Keys::kTargetTxnInLedger, section); std::uint32_t max = 0; - if (set(max, "maximum_txn_in_ledger", section)) + if (set(max, Keys::kMaximumTxnInLedger, section)) { if (max < setup.minimumTxnInLedger) { @@ -1909,7 +1910,7 @@ setupTxQ(Config const& config) moot. (There are other ways to do that, including minimum_txn_in_ledger_.) */ - set(setup.normalConsensusIncreasePercent, "normal_consensus_increase_percent", section); + set(setup.normalConsensusIncreasePercent, Keys::kNormalConsensusIncreasePercent, section); setup.normalConsensusIncreasePercent = std::clamp(setup.normalConsensusIncreasePercent, 0u, 1000u); @@ -1917,11 +1918,11 @@ setupTxQ(Config const& config) are nonsensical (uint overflows happen, so the limit grows instead of shrinking). 0 is not recommended. */ - set(setup.slowConsensusDecreasePercent, "slow_consensus_decrease_percent", section); + set(setup.slowConsensusDecreasePercent, Keys::kSlowConsensusDecreasePercent, section); setup.slowConsensusDecreasePercent = std::clamp(setup.slowConsensusDecreasePercent, 0u, 100u); - set(setup.maximumTxnPerAccount, "maximum_txn_per_account", section); - set(setup.minimumLastLedgerBuffer, "minimum_last_ledger_buffer", section); + set(setup.maximumTxnPerAccount, Keys::kMaximumTxnPerAccount, section); + set(setup.minimumLastLedgerBuffer, Keys::kMinimumLastLedgerBuffer, section); setup.standAlone = config.standalone(); return setup; diff --git a/src/xrpld/app/misc/detail/ValidatorKeys.cpp b/src/xrpld/app/misc/detail/ValidatorKeys.cpp index 0380e2e294..e439eca936 100644 --- a/src/xrpld/app/misc/detail/ValidatorKeys.cpp +++ b/src/xrpld/app/misc/detail/ValidatorKeys.cpp @@ -1,11 +1,11 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -17,18 +17,18 @@ namespace xrpl { ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j) { - if (config.exists(SECTION_VALIDATOR_TOKEN) && config.exists(SECTION_VALIDATION_SEED)) + if (config.exists(Sections::kValidatorToken) && config.exists(Sections::kValidationSeed)) { configInvalid_ = true; - JLOG(j.fatal()) << "Cannot specify both [" SECTION_VALIDATION_SEED - "] and [" SECTION_VALIDATOR_TOKEN "]"; + JLOG(j.fatal()) << "Cannot specify both [" << Sections::kValidationSeed << "] and [" + << Sections::kValidatorToken << "]"; return; } - if (config.exists(SECTION_VALIDATOR_TOKEN)) + if (config.exists(Sections::kValidatorToken)) { // token is non-const so it can be moved from - if (auto token = loadValidatorToken(config.section(SECTION_VALIDATOR_TOKEN).lines())) + if (auto token = loadValidatorToken(config.section(Sections::kValidatorToken).lines())) { auto const pk = derivePublicKey(KeyType::Secp256k1, token->validationSecret); auto const m = deserializeManifest(base64Decode(token->manifest)); @@ -36,7 +36,8 @@ ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j) if (!m || pk != m->signingKey) { configInvalid_ = true; - JLOG(j.fatal()) << "Invalid token specified in [" SECTION_VALIDATOR_TOKEN "]"; + JLOG(j.fatal()) << "Invalid token specified in [" << Sections::kValidatorToken + << "]"; } else { @@ -49,17 +50,17 @@ ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j) else { configInvalid_ = true; - JLOG(j.fatal()) << "Invalid token specified in [" SECTION_VALIDATOR_TOKEN "]"; + JLOG(j.fatal()) << "Invalid token specified in [" << Sections::kValidatorToken << "]"; } } - else if (config.exists(SECTION_VALIDATION_SEED)) + else if (config.exists(Sections::kValidationSeed)) { auto const seed = - parseBase58(config.section(SECTION_VALIDATION_SEED).lines().front()); + parseBase58(config.section(Sections::kValidationSeed).lines().front()); if (!seed) { configInvalid_ = true; - JLOG(j.fatal()) << "Invalid seed specified in [" SECTION_VALIDATION_SEED "]"; + JLOG(j.fatal()) << "Invalid seed specified in [" << Sections::kValidationSeed << "]"; } else { diff --git a/src/xrpld/app/misc/detail/setup_HashRouter.cpp b/src/xrpld/app/misc/detail/setup_HashRouter.cpp index 9727f5d733..97aa501698 100644 --- a/src/xrpld/app/misc/detail/setup_HashRouter.cpp +++ b/src/xrpld/app/misc/detail/setup_HashRouter.cpp @@ -2,8 +2,9 @@ #include -#include #include +#include +#include #include #include @@ -18,11 +19,11 @@ setupHashRouter(Config const& config) using namespace std::chrono; HashRouter::Setup setup; - auto const& section = config.section("hashrouter"); + auto const& section = config.section(Sections::kHashrouter); std::int32_t tmp{}; - if (set(tmp, "hold_time", section)) + if (set(tmp, Keys::kHoldTime, section)) { if (tmp < 12) { @@ -32,7 +33,7 @@ setupHashRouter(Config const& config) } setup.holdTime = seconds(tmp); } - if (set(tmp, "relay_time", section)) + if (set(tmp, Keys::kRelayTime, section)) { if (tmp < 8) { diff --git a/src/xrpld/app/rdb/backend/detail/Node.cpp b/src/xrpld/app/rdb/backend/detail/Node.cpp index 1fd3136420..9b7db6f0f5 100644 --- a/src/xrpld/app/rdb/backend/detail/Node.cpp +++ b/src/xrpld/app/rdb/backend/detail/Node.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include // IWYU pragma: keep @@ -1291,7 +1292,7 @@ bool dbHasSpace(soci::session& session, Config const& config, beast::Journal j) { boost::filesystem::space_info const space = - boost::filesystem::space(config.legacy("database_path")); + boost::filesystem::space(config.legacy(Sections::kDatabasePath)); if (space.available < megabytes(512)) { diff --git a/src/xrpld/app/rdb/detail/PeerFinder.cpp b/src/xrpld/app/rdb/detail/PeerFinder.cpp index 2481b63d2b..9452c3af99 100644 --- a/src/xrpld/app/rdb/detail/PeerFinder.cpp +++ b/src/xrpld/app/rdb/detail/PeerFinder.cpp @@ -2,11 +2,11 @@ #include -#include #include #include #include #include +#include #include #include // IWYU pragma: keep diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index a18b68a508..45b36808b2 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -1,9 +1,9 @@ #pragma once -#include #include #include #include +#include #include #include #include // VFALCO Breaks levelization diff --git a/src/xrpld/core/ConfigSections.h b/src/xrpld/core/ConfigSections.h deleted file mode 100644 index 7f22dd59c1..0000000000 --- a/src/xrpld/core/ConfigSections.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -namespace xrpl { - -// VFALCO DEPRECATED in favor of the BasicConfig interface -struct ConfigSection -{ - explicit ConfigSection() = default; - - static std::string - nodeDatabase() - { - return "node_db"; - } - static std::string - importNodeDatabase() - { - return "import_db"; - } -}; - -// VFALCO TODO Rename and replace these macros with variables. -#define SECTION_AMENDMENTS "amendments" -#define SECTION_AMENDMENT_MAJORITY_TIME "amendment_majority_time" -#define SECTION_BETA_RPC_API "beta_rpc_api" -#define SECTION_CLUSTER_NODES "cluster_nodes" -#define SECTION_COMPRESSION "compression" -#define SECTION_DEBUG_LOGFILE "debug_logfile" -#define SECTION_ELB_SUPPORT "elb_support" -#define SECTION_FEE_DEFAULT "fee_default" -#define SECTION_FETCH_DEPTH "fetch_depth" -#define SECTION_INSIGHT "insight" -#define SECTION_IO_WORKERS "io_workers" -#define SECTION_IPS "ips" -#define SECTION_IPS_FIXED "ips_fixed" -#define SECTION_LEDGER_HISTORY "ledger_history" -#define SECTION_LEDGER_REPLAY "ledger_replay" -#define SECTION_MAX_TRANSACTIONS "max_transactions" -#define SECTION_NETWORK_ID "network_id" -#define SECTION_NETWORK_QUORUM "network_quorum" -#define SECTION_NODE_SEED "node_seed" -#define SECTION_NODE_SIZE "node_size" -#define SECTION_OVERLAY "overlay" -#define SECTION_PATH_SEARCH_OLD "path_search_old" -#define SECTION_PATH_SEARCH "path_search" -#define SECTION_PATH_SEARCH_FAST "path_search_fast" -#define SECTION_PATH_SEARCH_MAX "path_search_max" -#define SECTION_PEER_PRIVATE "peer_private" -#define SECTION_PEERS_MAX "peers_max" -#define SECTION_PEERS_IN_MAX "peers_in_max" -#define SECTION_PEERS_OUT_MAX "peers_out_max" -#define SECTION_PORT_GRPC "port_grpc" -#define SECTION_PREFETCH_WORKERS "prefetch_workers" -#define SECTION_REDUCE_RELAY "reduce_relay" -#define SECTION_RELATIONAL_DB "relational_db" -#define SECTION_RELAY_PROPOSALS "relay_proposals" -#define SECTION_RELAY_VALIDATIONS "relay_validations" -#define SECTION_RPC_STARTUP "rpc_startup" -#define SECTION_SIGNING_SUPPORT "signing_support" -#define SECTION_SNTP "sntp_servers" -#define SECTION_SSL_VERIFY "ssl_verify" -#define SECTION_SSL_VERIFY_FILE "ssl_verify_file" -#define SECTION_SSL_VERIFY_DIR "ssl_verify_dir" -#define SECTION_SERVER_DOMAIN "server_domain" -#define SECTION_SWEEP_INTERVAL "sweep_interval" -#define SECTION_VALIDATORS_FILE "validators_file" -#define SECTION_VALIDATION_SEED "validation_seed" -#define SECTION_VALIDATOR_KEYS "validator_keys" -#define SECTION_VALIDATOR_KEY_REVOCATION "validator_key_revocation" -#define SECTION_VALIDATOR_LIST_KEYS "validator_list_keys" -#define SECTION_VALIDATOR_LIST_SITES "validator_list_sites" -#define SECTION_VALIDATOR_LIST_THRESHOLD "validator_list_threshold" -#define SECTION_VALIDATORS "validators" -#define SECTION_VALIDATOR_TOKEN "validator_token" -#define SECTION_VETO_AMENDMENTS "veto_amendments" -#define SECTION_WORKERS "workers" - -} // namespace xrpl diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index 6eedc43edd..1b2449823e 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -1,8 +1,5 @@ #include -#include - -#include #include #include #include @@ -11,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -385,7 +384,7 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand load(); { // load() may have set a new value for the dataDir - std::string const dbPath(legacy("database_path")); + std::string const dbPath(legacy(Sections::kDatabasePath)); if (!dbPath.empty()) { dataDir = boost::filesystem::path(dbPath); @@ -404,7 +403,7 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand if (ec) Throw(boost::str(boost::format("Can not create %s") % dataDir)); - legacy("database_path", boost::filesystem::absolute(dataDir).string()); + legacy(Sections::kDatabasePath, boost::filesystem::absolute(dataDir).string()); } HTTPClient::initializeSSLContext(this->sslVerifyDir, this->sslVerifyFile, this->sslVerify, j_); @@ -412,11 +411,11 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand if (runStandalone_) ledgerHistory = 0; - Section const ledgerTxTablesSection = section("ledger_tx_tables"); - getIfExists(ledgerTxTablesSection, "use_tx_tables", useTxTables_); + Section const ledgerTxTablesSection = section(Sections::kLedgerTxTables); + getIfExists(ledgerTxTablesSection, Keys::kUseTxTables, useTxTables_); - Section const& nodeDbSection{section(ConfigSection::nodeDatabase())}; - getIfExists(nodeDbSection, "fast_load", fastLoad); + Section const& nodeDbSection{section(Sections::kNodeDatabase)}; + getIfExists(nodeDbSection, Keys::kFastLoad, fastLoad); } // 0 ports are allowed for unit tests, but still not allowed to be present in @@ -424,16 +423,16 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand static void checkZeroPorts(Config const& config) { - if (!config.exists("server")) + if (!config.exists(Sections::kServer)) return; - for (auto const& name : config.section("server").values()) + for (auto const& name : config.section(Sections::kServer).values()) { if (!config.exists(name)) return; auto const& section = config[name]; - auto const optResult = section.get("port"); + auto const optResult = section.get(Keys::kPort); if (optResult) { auto const port = beast::lexicalCast(*optResult); @@ -477,10 +476,10 @@ Config::loadFromString(std::string const& fileContents) build(secConfig); - if (auto s = getIniFileSection(secConfig, SECTION_IPS)) + if (auto s = getIniFileSection(secConfig, Sections::kIps)) ips = *s; - if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED)) + if (auto s = getIniFileSection(secConfig, Sections::kIpsFixed)) ipsFixed = *s; // if the user has specified ip:port then replace : with a space. @@ -507,16 +506,16 @@ Config::loadFromString(std::string const& fileContents) { std::string dbPath; - if (getSingleSection(secConfig, "database_path", dbPath, j_)) + if (getSingleSection(secConfig, Sections::kDatabasePath, dbPath, j_)) { boost::filesystem::path const p(dbPath); - legacy("database_path", boost::filesystem::absolute(p).string()); + legacy(Sections::kDatabasePath, boost::filesystem::absolute(p).string()); } } std::string strTemp; - if (getSingleSection(secConfig, SECTION_NETWORK_ID, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kNetworkId, strTemp, j_)) { if (strTemp == "main") { @@ -536,43 +535,45 @@ Config::loadFromString(std::string const& fileContents) } } - if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kPeerPrivate, strTemp, j_)) peerPrivate = beast::lexicalCastThrow(strTemp); - if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kPeersMax, strTemp, j_)) { peersMax = beast::lexicalCastThrow(strTemp); } else { std::optional peersInMaxOpt{}; - if (getSingleSection(secConfig, SECTION_PEERS_IN_MAX, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kPeersInMax, strTemp, j_)) { peersInMaxOpt = beast::lexicalCastThrow(strTemp); if (*peersInMaxOpt > 1000) { - Throw("Invalid value specified in [" SECTION_PEERS_IN_MAX - "] section; the value must be less or equal than 1000"); + Throw( + std::string("Invalid value specified in [") + Sections::kPeersInMax + + "] section; the value must be less or equal than 1000"); } } std::optional peersOutMaxOpt{}; - if (getSingleSection(secConfig, SECTION_PEERS_OUT_MAX, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kPeersOutMax, strTemp, j_)) { peersOutMaxOpt = beast::lexicalCastThrow(strTemp); if (*peersOutMaxOpt < 10 || *peersOutMaxOpt > 1000) { - Throw("Invalid value specified in [" SECTION_PEERS_OUT_MAX - "] section; the value must be in range 10-1000"); + Throw( + std::string("Invalid value specified in [") + Sections::kPeersOutMax + + "] section; the value must be in range 10-1000"); } } // if one section is configured then the other must be configured too if ((peersInMaxOpt && !peersOutMaxOpt) || (peersOutMaxOpt && !peersInMaxOpt)) { - Throw("Both sections [" SECTION_PEERS_IN_MAX - "]" - "and [" SECTION_PEERS_OUT_MAX "] must be configured"); + Throw( + std::string("Both sections [") + Sections::kPeersInMax + "]" + " and [" + + Sections::kPeersOutMax + "] must be configured"); } if (peersInMaxOpt && peersOutMaxOpt) @@ -582,7 +583,7 @@ Config::loadFromString(std::string const& fileContents) } } - if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kNodeSize, strTemp, j_)) { if (boost::iequals(strTemp, "tiny")) { @@ -610,19 +611,19 @@ Config::loadFromString(std::string const& fileContents) } } - if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kSigningSupport, strTemp, j_)) signingEnabled_ = beast::lexicalCastThrow(strTemp); - if (getSingleSection(secConfig, SECTION_ELB_SUPPORT, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kElbSupport, strTemp, j_)) elbSupport = beast::lexicalCastThrow(strTemp); - getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, sslVerifyFile, j_); - getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, sslVerifyDir, j_); + getSingleSection(secConfig, Sections::kSslVerifyFile, sslVerifyFile, j_); + getSingleSection(secConfig, Sections::kSslVerifyDir, sslVerifyDir, j_); - if (getSingleSection(secConfig, SECTION_SSL_VERIFY, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kSslVerify, strTemp, j_)) sslVerify = beast::lexicalCastThrow(strTemp); - if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kRelayValidations, strTemp, j_)) { if (boost::iequals(strTemp, "all")) { @@ -638,12 +639,13 @@ Config::loadFromString(std::string const& fileContents) } else { - Throw("Invalid value specified in [" SECTION_RELAY_VALIDATIONS - "] section"); + Throw( + std::string("Invalid value specified in [") + Sections::kRelayValidations + + "] section"); } } - if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kRelayProposals, strTemp, j_)) { if (boost::iequals(strTemp, "all")) { @@ -659,28 +661,30 @@ Config::loadFromString(std::string const& fileContents) } else { - Throw("Invalid value specified in [" SECTION_RELAY_PROPOSALS - "] section"); + Throw( + std::string("Invalid value specified in [") + Sections::kRelayProposals + + "] section"); } } - if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN)) + if (exists(Sections::kValidationSeed) && exists(Sections::kValidatorToken)) { - Throw("Cannot have both [" SECTION_VALIDATION_SEED - "] and [" SECTION_VALIDATOR_TOKEN "] config sections"); + Throw( + std::string("Cannot have both [") + Sections::kValidationSeed + "] and [" + + Sections::kValidatorToken + "] config sections"); } - if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kNetworkQuorum, strTemp, j_)) networkQuorum = beast::lexicalCastThrow(strTemp); - fees = setupFeeVote(section("voting")); + fees = setupFeeVote(section(Sections::kVoting)); /* [fee_default] is documented in the example config files as useful for * things like offline transaction signing. Until that's completely * deprecated, allow it to override the [voting] section. */ - if (getSingleSection(secConfig, SECTION_FEE_DEFAULT, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kFeeDefault, strTemp, j_)) fees.referenceFee = beast::lexicalCastThrow(strTemp); - if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kLedgerHistory, strTemp, j_)) { if (boost::iequals(strTemp, "full")) { @@ -696,7 +700,7 @@ Config::loadFromString(std::string const& fileContents) } } - if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kFetchDepth, strTemp, j_)) { if (boost::iequals(strTemp, "none")) { @@ -716,74 +720,78 @@ Config::loadFromString(std::string const& fileContents) // By default, validators don't have pathfinding enabled, unless it is // explicitly requested by the server's admin. - if (exists(SECTION_VALIDATION_SEED) || exists(SECTION_VALIDATOR_TOKEN)) + if (exists(Sections::kValidationSeed) || exists(Sections::kValidatorToken)) pathSearchMax = 0; - if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kPathSearchOld, strTemp, j_)) pathSearchOld = beast::lexicalCastThrow(strTemp); - if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kPathSearch, strTemp, j_)) pathSearch = beast::lexicalCastThrow(strTemp); - if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kPathSearchFast, strTemp, j_)) pathSearchFast = beast::lexicalCastThrow(strTemp); - if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kPathSearchMax, strTemp, j_)) pathSearchMax = beast::lexicalCastThrow(strTemp); - if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kDebugLogfile, strTemp, j_)) debugLogfile_ = strTemp; - if (getSingleSection(secConfig, SECTION_SWEEP_INTERVAL, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kSweepInterval, strTemp, j_)) { sweepInterval = beast::lexicalCastThrow(strTemp); if (sweepInterval < 10 || sweepInterval > 600) { - Throw("Invalid " SECTION_SWEEP_INTERVAL - ": must be between 10 and 600 inclusive"); + Throw( + std::string("Invalid ") + Sections::kSweepInterval + + ": must be between 10 and 600 inclusive"); } } - if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kWorkers, strTemp, j_)) { workers = beast::lexicalCastThrow(strTemp); if (workers < 1 || workers > 1024) { - Throw("Invalid " SECTION_WORKERS - ": must be between 1 and 1024 inclusive."); + Throw( + std::string("Invalid ") + Sections::kWorkers + + ": must be between 1 and 1024 inclusive."); } } - if (getSingleSection(secConfig, SECTION_IO_WORKERS, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kIoWorkers, strTemp, j_)) { ioWorkers = beast::lexicalCastThrow(strTemp); if (ioWorkers < 1 || ioWorkers > 1024) { - Throw("Invalid " SECTION_IO_WORKERS - ": must be between 1 and 1024 inclusive."); + Throw( + std::string("Invalid ") + Sections::kIoWorkers + + ": must be between 1 and 1024 inclusive."); } } - if (getSingleSection(secConfig, SECTION_PREFETCH_WORKERS, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kPrefetchWorkers, strTemp, j_)) { prefetchWorkers = beast::lexicalCastThrow(strTemp); if (prefetchWorkers < 1 || prefetchWorkers > 1024) { - Throw("Invalid " SECTION_PREFETCH_WORKERS - ": must be between 1 and 1024 inclusive."); + Throw( + std::string("Invalid ") + Sections::kPrefetchWorkers + + ": must be between 1 and 1024 inclusive."); } } - if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kCompression, strTemp, j_)) compression = beast::lexicalCastThrow(strTemp); - if (getSingleSection(secConfig, SECTION_LEDGER_REPLAY, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kLedgerReplay, strTemp, j_)) ledgerReplay = beast::lexicalCastThrow(strTemp); - if (exists(SECTION_REDUCE_RELAY)) + if (exists(Sections::kReduceRelay)) { - auto sec = section(SECTION_REDUCE_RELAY); + auto sec = section(Sections::kReduceRelay); ///////////////////// !!TEMPORARY CODE BLOCK!! //////////////////////// // vp_enable config option is deprecated by vp_base_squelch_enable // @@ -791,22 +799,23 @@ Config::loadFromString(std::string const& fileContents) // is the default algorithm, it must be replaced with: // // VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = // // sec.value_or("vp_base_squelch_enable", true); // - if (sec.exists("vp_base_squelch_enable") && sec.exists("vp_enable")) + if (sec.exists(Keys::kVpBaseSquelchEnable) && sec.exists(Keys::kVpEnable)) { - Throw("Invalid " SECTION_REDUCE_RELAY - " cannot specify both vp_base_squelch_enable and vp_enable " - "options. " - "vp_enable was deprecated and replaced by " - "vp_base_squelch_enable"); + Throw( + std::string("Invalid ") + Sections::kReduceRelay + + " cannot specify both vp_base_squelch_enable and vp_enable " + "options. " + "vp_enable was deprecated and replaced by " + "vp_base_squelch_enable"); } - if (sec.exists("vp_base_squelch_enable")) + if (sec.exists(Keys::kVpBaseSquelchEnable)) { - vpReduceRelayBaseSquelchEnable = sec.valueOr("vp_base_squelch_enable", false); + vpReduceRelayBaseSquelchEnable = sec.valueOr(Keys::kVpBaseSquelchEnable, false); } - else if (sec.exists("vp_enable")) + else if (sec.exists(Keys::kVpEnable)) { - vpReduceRelayBaseSquelchEnable = sec.valueOr("vp_enable", false); + vpReduceRelayBaseSquelchEnable = sec.valueOr(Keys::kVpEnable, false); } else { @@ -818,97 +827,103 @@ Config::loadFromString(std::string const& fileContents) // 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. // - vpReduceRelaySquelchMaxSelectedPeers = sec.valueOr("vp_base_squelch_max_selected_peers", 5); + vpReduceRelaySquelchMaxSelectedPeers = sec.valueOr(Keys::kVpBaseSquelchMaxSelectedPeers, 5); if (vpReduceRelaySquelchMaxSelectedPeers < 3) { - Throw("Invalid " SECTION_REDUCE_RELAY - " vp_base_squelch_max_selected_peers must be " - "greater than or equal to 3"); + Throw( + std::string("Invalid ") + Sections::kReduceRelay + + " vp_base_squelch_max_selected_peers must be " + "greater than or equal to 3"); } ///////////////// !!END OF TEMPORARY CODE BLOCK!! ///////////////////// - txReduceRelayEnable = sec.valueOr("tx_enable", false); - txReduceRelayMetrics = sec.valueOr("tx_metrics", false); - txReduceRelayMinPeers = sec.valueOr("tx_min_peers", 20); - txRelayPercentage = sec.valueOr("tx_relay_percentage", 25); + txReduceRelayEnable = sec.valueOr(Keys::kTxEnable, false); + txReduceRelayMetrics = sec.valueOr(Keys::kTxMetrics, false); + txReduceRelayMinPeers = sec.valueOr(Keys::kTxMinPeers, 20); + txRelayPercentage = sec.valueOr(Keys::kTxRelayPercentage, 25); if (txRelayPercentage < 10 || txRelayPercentage > 100 || txReduceRelayMinPeers < 10) { - Throw("Invalid " SECTION_REDUCE_RELAY - ", tx_min_peers must be greater than or equal to 10" - ", tx_relay_percentage must be greater than or equal to 10 " - "and less than or equal to 100"); + Throw( + std::string("Invalid ") + Sections::kReduceRelay + + ", tx_min_peers must be greater than or equal to 10" + ", tx_relay_percentage must be greater than or equal to 10 " + "and less than or equal to 100"); } } - if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kMaxTransactions, strTemp, j_)) { maxTransactions = std::clamp(beast::lexicalCastThrow(strTemp), kMinJobQueueTx, kMaxJobQueueTx); } - if (getSingleSection(secConfig, SECTION_SERVER_DOMAIN, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kServerDomain, strTemp, j_)) { if (!isProperlyFormedTomlDomain(strTemp)) { Throw( - "Invalid " SECTION_SERVER_DOMAIN + std::string("Invalid ") + Sections::kServerDomain + ": the domain name does not appear to meet the requirements."); } serverDomain = strTemp; } - if (exists(SECTION_OVERLAY)) + if (exists(Sections::kOverlay)) { - auto const sec = section(SECTION_OVERLAY); + auto const sec = section(Sections::kOverlay); using namespace std::chrono; try { - if (auto val = sec.get("max_unknown_time")) + if (auto val = sec.get(Keys::kMaxUnknownTime)) maxUnknownTime = seconds{beast::lexicalCastThrow(*val)}; } catch (...) { - Throw("Invalid value 'max_unknown_time' in " SECTION_OVERLAY - ": must be of the form '' representing seconds."); + Throw( + std::string("Invalid value 'max_unknown_time' in ") + Sections::kOverlay + + ": must be of the form '' representing seconds."); } if (maxUnknownTime < seconds{300} || maxUnknownTime > seconds{1800}) { Throw( - "Invalid value 'max_unknown_time' in " SECTION_OVERLAY + std::string("Invalid value 'max_unknown_time' in ") + Sections::kOverlay + ": the time must be between 300 and 1800 seconds, inclusive."); } try { - if (auto val = sec.get("max_diverged_time")) + if (auto val = sec.get(Keys::kMaxDivergedTime)) maxDivergedTime = seconds{beast::lexicalCastThrow(*val)}; } catch (...) { - Throw("Invalid value 'max_diverged_time' in " SECTION_OVERLAY - ": must be of the form '' representing seconds."); + Throw( + std::string("Invalid value 'max_diverged_time' in ") + Sections::kOverlay + + ": must be of the form '' representing seconds."); } if (maxDivergedTime < seconds{60} || maxDivergedTime > seconds{900}) { - Throw("Invalid value 'max_diverged_time' in " SECTION_OVERLAY - ": the time must be between 60 and 900 seconds, inclusive."); + Throw( + std::string("Invalid value 'max_diverged_time' in ") + Sections::kOverlay + + ": the time must be between 60 and 900 seconds, inclusive."); } } - if (getSingleSection(secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kAmendmentMajorityTime, strTemp, j_)) { using namespace std::chrono; boost::regex const re("^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$"); boost::smatch match; if (!boost::regex_match(strTemp, match, re)) { - Throw("Invalid " SECTION_AMENDMENT_MAJORITY_TIME - ", must be: [0-9]+ [minutes|hours|days|weeks]"); + Throw( + std::string("Invalid ") + Sections::kAmendmentMajorityTime + + ", must be: [0-9]+ [minutes|hours|days|weeks]"); } std::uint32_t const duration = beast::lexicalCastThrow(match[1].str()); @@ -932,13 +947,14 @@ Config::loadFromString(std::string const& fileContents) if (amendmentMajorityTime < minutes(15)) { - Throw("Invalid " SECTION_AMENDMENT_MAJORITY_TIME - ", the minimum amount of time an amendment must hold a " - "majority is 15 minutes"); + Throw( + std::string("Invalid ") + Sections::kAmendmentMajorityTime + + ", the minimum amount of time an amendment must hold a " + "majority is 15 minutes"); } } - if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kBetaRpcApi, strTemp, j_)) betaRpcApi = beast::lexicalCastThrow(strTemp); // Do not load trusted validator configuration for standalone mode @@ -954,14 +970,14 @@ Config::loadFromString(std::string const& fileContents) // if we can't find it. boost::filesystem::path validatorsFile; - if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_)) + if (getSingleSection(secConfig, Sections::kValidatorsFile, strTemp, j_)) { validatorsFile = strTemp; if (validatorsFile.empty()) { - Throw("Invalid path specified in [" SECTION_VALIDATORS_FILE - "]"); + Throw( + std::string("Invalid path specified in [") + Sections::kValidatorsFile + "]"); } if (!validatorsFile.is_absolute() && !configDir.empty()) @@ -970,7 +986,7 @@ Config::loadFromString(std::string const& fileContents) if (!boost::filesystem::exists(validatorsFile)) { Throw( - "The file specified in [" SECTION_VALIDATORS_FILE + std::string("The file specified in [") + Sections::kValidatorsFile + "] " "does not exist: " + validatorsFile.string()); @@ -980,8 +996,8 @@ Config::loadFromString(std::string const& fileContents) !boost::filesystem::is_symlink(validatorsFile)) { Throw( - "Invalid file specified in [" SECTION_VALIDATORS_FILE "]: " + - validatorsFile.string()); + std::string("Invalid file specified in [") + Sections::kValidatorsFile + + "]: " + validatorsFile.string()); } } else if (!configDir.empty()) @@ -1018,41 +1034,44 @@ Config::loadFromString(std::string const& fileContents) auto iniFile = parseIniFile(data, true); - auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS); + auto entries = getIniFileSection(iniFile, Sections::kValidators); if (entries != nullptr) - section(SECTION_VALIDATORS).append(*entries); + section(Sections::kValidators).append(*entries); - auto valKeyEntries = getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS); + auto valKeyEntries = getIniFileSection(iniFile, Sections::kValidatorKeys); if (valKeyEntries != nullptr) - section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries); + section(Sections::kValidatorKeys).append(*valKeyEntries); - auto valSiteEntries = getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES); + auto valSiteEntries = getIniFileSection(iniFile, Sections::kValidatorListSites); if (valSiteEntries != nullptr) - section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries); + section(Sections::kValidatorListSites).append(*valSiteEntries); - auto valListKeys = getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS); + auto valListKeys = getIniFileSection(iniFile, Sections::kValidatorListKeys); if (valListKeys != nullptr) - section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys); + section(Sections::kValidatorListKeys).append(*valListKeys); - auto valListThreshold = getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_THRESHOLD); + auto valListThreshold = getIniFileSection(iniFile, Sections::kValidatorListThreshold); if (valListThreshold != nullptr) - section(SECTION_VALIDATOR_LIST_THRESHOLD).append(*valListThreshold); + section(Sections::kValidatorListThreshold).append(*valListThreshold); if ((entries == nullptr) && (valKeyEntries == nullptr) && (valListKeys == nullptr)) { Throw( - "The file specified in [" SECTION_VALIDATORS_FILE + std::string("The file specified in [") + Sections::kValidatorsFile + "] " - "does not contain a [" SECTION_VALIDATORS + "does not contain a [" + + Sections::kValidators + "], " - "[" SECTION_VALIDATOR_KEYS + "[" + + Sections::kValidatorKeys + "] or " - "[" SECTION_VALIDATOR_LIST_KEYS + "[" + + Sections::kValidatorListKeys + "]" " section: " + validatorsFile.string()); @@ -1060,7 +1079,7 @@ Config::loadFromString(std::string const& fileContents) } validatorListThreshold = [&]() -> std::optional { - auto const& listThreshold = section(SECTION_VALIDATOR_LIST_THRESHOLD); + auto const& listThreshold = section(Sections::kValidatorListThreshold); if (listThreshold.lines().empty()) { return std::nullopt; @@ -1073,34 +1092,38 @@ Config::loadFromString(std::string const& fileContents) { return std::nullopt; // NOTE: Explicitly ask for computed } - if (listThreshold > section(SECTION_VALIDATOR_LIST_KEYS).values().size()) + if (listThreshold > section(Sections::kValidatorListKeys).values().size()) { Throw( - "Value in config section " - "[" SECTION_VALIDATOR_LIST_THRESHOLD + std::string( + "Value in config section " + "[") + + Sections::kValidatorListThreshold + "] exceeds the number of configured list keys"); } return listThreshold; } Throw( - "Config section " - "[" SECTION_VALIDATOR_LIST_THRESHOLD "] should contain single value only"); + std::string( + "Config section " + "[") + + Sections::kValidatorListThreshold + "] should contain single value only"); }(); // Consolidate [validator_keys] and [validators] - section(SECTION_VALIDATORS).append(section(SECTION_VALIDATOR_KEYS).lines()); + section(Sections::kValidators).append(section(Sections::kValidatorKeys).lines()); - if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() && - section(SECTION_VALIDATOR_LIST_KEYS).lines().empty()) + if (!section(Sections::kValidatorListSites).lines().empty() && + section(Sections::kValidatorListKeys).lines().empty()) { Throw( - "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) + "] config section is missing"); + "[" + std::string(Sections::kValidatorListKeys) + "] config section is missing"); } } { - auto const part = section("features"); + auto const part = section(Sections::kFeatures); for (auto const& s : part.values()) { if (auto const f = getRegisteredFeature(s)) @@ -1182,15 +1205,15 @@ setupFeeVote(Section const& section) FeeSetup setup; { std::uint64_t temp = 0; - if (set(temp, "reference_fee", section) && + if (set(temp, Keys::kReferenceFee, section) && temp <= std::numeric_limits::max()) setup.referenceFee = temp; } { std::uint32_t temp = 0; - if (set(temp, "account_reserve", section)) + if (set(temp, Keys::kAccountReserve, section)) setup.accountReserve = temp; - if (set(temp, "owner_reserve", section)) + if (set(temp, Keys::kOwnerReserve, section)) setup.ownerReserve = temp; } return setup; @@ -1203,7 +1226,7 @@ setupDatabaseCon(Config const& c, std::optional j) setup.startUp = c.startUp; setup.standAlone = c.standalone(); - setup.dataDir = c.legacy("database_path"); + setup.dataDir = c.legacy(Sections::kDatabasePath); if (!setup.standAlone && setup.dataDir.empty()) { Throw("database_path must be set."); @@ -1211,7 +1234,7 @@ setupDatabaseCon(Config const& c, std::optional j) if (!setup.globalPragma) { - auto const& sqlite = c.section("sqlite"); + auto const& sqlite = c.section(Sections::kSqlite); auto result = std::make_unique>(); result->reserve(3); @@ -1328,11 +1351,11 @@ setupDatabaseCon(Config const& c, std::optional j) // TX Pragma int64_t pageSize = 4096; int64_t journalSizeLimit = 1582080; - if (c.exists("sqlite")) + if (c.exists(Sections::kSqlite)) { - auto& s = c.section("sqlite"); - set(journalSizeLimit, "journal_size_limit", s); - set(pageSize, "page_size", s); + auto& s = c.section(Sections::kSqlite); + set(journalSizeLimit, Keys::kJournalSizeLimit, s); + set(pageSize, Keys::kPageSize, s); if (pageSize < 512 || pageSize > 65536) Throw("Invalid page_size. Must be between 512 and 65536."); diff --git a/src/xrpld/overlay/Cluster.h b/src/xrpld/overlay/Cluster.h index 982f11aaae..a8c2083fbc 100644 --- a/src/xrpld/overlay/Cluster.h +++ b/src/xrpld/overlay/Cluster.h @@ -2,9 +2,9 @@ #include -#include #include #include +#include #include #include diff --git a/src/xrpld/overlay/detail/Cluster.cpp b/src/xrpld/overlay/detail/Cluster.cpp index 7855c9647d..15c8fa9c66 100644 --- a/src/xrpld/overlay/detail/Cluster.cpp +++ b/src/xrpld/overlay/detail/Cluster.cpp @@ -2,11 +2,11 @@ #include -#include #include #include #include #include +#include #include #include diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index b71cef6719..89c7dfe5eb 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -36,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -96,7 +97,7 @@ static constexpr auto kDisabled = 0; static constexpr auto kOverlay = (1 << 0); static constexpr auto kServerInfo = (1 << 1); static constexpr auto kServerCounts = (1 << 2); -static constexpr auto kUNL = (1 << 3); +static constexpr auto kUnl = (1 << 3); } // namespace CrawlOptions //------------------------------------------------------------------------------ @@ -885,7 +886,7 @@ OverlayImpl::processCrawl(http_request_type const& req, Handoff& handoff) { msg.body()["counts"] = getServerCounts(); } - if ((setup_.crawlOptions & CrawlOptions::kUNL) != 0u) + if ((setup_.crawlOptions & CrawlOptions::kUnl) != 0u) { msg.body()["unl"] = getUnlInfo(); } @@ -1516,7 +1517,7 @@ setupOverlay(BasicConfig const& config, beast::Journal j) Overlay::Setup setup; { - auto const& section = config.section("overlay"); + auto const& section = config.section(Sections::kOverlay); setup.context = makeSslContext(""); set(setup.ipLimit, "ip_limit", section); @@ -1543,7 +1544,7 @@ setupOverlay(BasicConfig const& config, beast::Journal j) } { - auto const& section = config.section("crawl"); + auto const& section = config.section(Sections::kCrawl); auto const& values = section.values(); if (values.size() > 1) @@ -1569,33 +1570,33 @@ setupOverlay(BasicConfig const& config, beast::Journal j) if (crawlEnabled) { - if (get(section, "overlay", true)) + if (get(section, Keys::kOverlay, true)) { setup.crawlOptions |= CrawlOptions::kOverlay; } - if (get(section, "server", true)) + if (get(section, Keys::kServer, true)) { setup.crawlOptions |= CrawlOptions::kServerInfo; } - if (get(section, "counts", false)) + if (get(section, Keys::kCounts, false)) { setup.crawlOptions |= CrawlOptions::kServerCounts; } - if (get(section, "unl", true)) + if (get(section, Keys::kUnl, true)) { - setup.crawlOptions |= CrawlOptions::kUNL; + setup.crawlOptions |= CrawlOptions::kUnl; } } } { - auto const& section = config.section("vl"); + auto const& section = config.section(Sections::kVl); set(setup.vlEnabled, "enabled", section); } try { - auto id = config.legacy("network_id"); + auto id = config.legacy(Sections::kNetworkId); if (!id.empty()) { diff --git a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp index f51f3630eb..9dbedfe4f1 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp @@ -7,13 +7,13 @@ #include #include -#include #include #include #include #include #include #include +#include #include #include diff --git a/src/xrpld/perflog/detail/PerfLogImp.cpp b/src/xrpld/perflog/detail/PerfLogImp.cpp index 60b6efc0a9..5ace4d8c8b 100644 --- a/src/xrpld/perflog/detail/PerfLogImp.cpp +++ b/src/xrpld/perflog/detail/PerfLogImp.cpp @@ -1,11 +1,12 @@ #include -#include #include #include #include #include #include +#include +#include #include #include #include @@ -489,7 +490,7 @@ setupPerfLog(Section const& section, boost::filesystem::path const& configDir) } std::uint64_t logInterval = 0; - if (getIfExists(section, "log_interval", logInterval)) + if (getIfExists(section, Keys::kLogInterval, logInterval)) setup.logInterval = std::chrono::seconds(logInterval); return setup; } diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index c73e474e18..5177c85738 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include @@ -16,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1128,16 +1128,16 @@ parsePorts(Config const& config, std::ostream& log) { std::vector result; - if (!config.exists("server")) + if (!config.exists(Sections::kServer)) { log << "Required section [server] is missing"; Throw(); } ParsedPort common; - parsePort(common, config["server"], log); + parsePort(common, config[Sections::kServer], log); - auto const& names = config.section("server").values(); + auto const& names = config.section(Sections::kServer).values(); result.reserve(names.size()); for (auto const& name : names) { @@ -1149,7 +1149,7 @@ parsePorts(Config const& config, std::ostream& log) // grpc ports are parsed by GRPCServer class. Do not validate // grpc port information in this file. - if (name == SECTION_PORT_GRPC) + if (name == Sections::kPortGrpc) continue; ParsedPort parsed = common; From 0fb1aca461c775b2f5e0334dcff9ded3571634d7 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 9 Jun 2026 19:02:06 +0200 Subject: [PATCH 74/87] refactor: Introduce XRPL_ASSERT_IF for amendment-gated assertions (#7378) Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com> --- include/xrpl/beast/utility/instrumentation.h | 12 +++ src/libxrpl/ledger/helpers/LendingHelpers.cpp | 84 +++++++++---------- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 8 +- 3 files changed, 57 insertions(+), 47 deletions(-) diff --git a/include/xrpl/beast/utility/instrumentation.h b/include/xrpl/beast/utility/instrumentation.h index 39b80bc438..c20c42156a 100644 --- a/include/xrpl/beast/utility/instrumentation.h +++ b/include/xrpl/beast/utility/instrumentation.h @@ -11,6 +11,8 @@ // Macros below are copied from antithesis_sdk.h and slightly simplified // The duplication is because Visual Studio 2019 cannot compile that header // even with the option -Zc:__cplusplus added. +// NOTE: cond must not contain bare commas outside () or []. Commas inside {} +// are not protected by the preprocessor and would be parsed as extra arguments. #define ALWAYS(cond, message, ...) assert((message) && (cond)) #define ALWAYS_OR_UNREACHABLE(cond, message) assert((message) && (cond)) #define SOMETIMES(cond, message, ...) @@ -22,6 +24,8 @@ #define XRPL_ASSERT_PARTS(cond, function, description, ...) \ XRPL_ASSERT(cond, function " : " description) +#define XRPL_ASSERT_IF(guard, cond, message) XRPL_ASSERT(!(guard) || (cond), message) + // How to use the instrumentation macros: // // * XRPL_ASSERT if cond must be true but the line might not be reached during @@ -29,6 +33,14 @@ // * XRPL_ASSERT_PARTS is for convenience, and works like XRPL_ASSERT, but // splits the message param into "function" and "description", then joins // them with " : " before passing to XRPL_ASSERT. +// * XRPL_ASSERT_IF(guard, cond, message) asserts the implication +// `guard => cond`: it can only fail when guard is true (e.g. an amendment +// is enabled) and cond is false. Unlike `if (guard) XRPL_ASSERT(...)`, the +// assertion site is always evaluated, so the fuzzer registers it +// unconditionally; cond itself is short-circuited and only evaluated when +// guard is true. NOTE: do not rely on side effects in guard — in release +// builds the assertion body is stripped, and the compiler may optimize away +// a side-effect-free guard entirely. // * ALWAYS if cond must be true _and_ the line must be reached during fuzzing. // Same like `assert` in normal use. // * REACHABLE if the line must be reached during fuzzing diff --git a/src/libxrpl/ledger/helpers/LendingHelpers.cpp b/src/libxrpl/ledger/helpers/LendingHelpers.cpp index 15ebf33e46..0a195f9cbe 100644 --- a/src/libxrpl/ledger/helpers/LendingHelpers.cpp +++ b/src/libxrpl/ledger/helpers/LendingHelpers.cpp @@ -798,44 +798,44 @@ doOverpayment( // (P * factor) / factor round-trip can leave the new principal one // scale-unit high, so these equalities do not hold on the pre-amendment // code path and must be gated to match the fix they verify. - if (rules.enabled(fixCleanup3_2_0)) - { - // The valueChange returned by tryOverpayment satisfies - // valueChange = (newInterestDue - oldInterestDue) + untrackedInterest. - // Using the loan-state identity v = p + i + m and the adjacent - // `principal change agrees` assertion (dp = oldP - newP), this - // rearranges into three independently-computable terms: - // - // 1. TVO change beyond what principal repayment alone explains: - // newTVO - (oldTVO - dp) - // 2. Management fee released by re-amortization (positive when - // mfee decreased; zero when managementFeeRate == 0): - // oldMfee - newMfee - // 3. The overpayment's penalty interest part (= untrackedInterest - // for the overpayment path; see computeOverpaymentComponents): - // trackedInterestPart() - [[maybe_unused]] Number const tvoChange = newRoundedLoanState.valueOutstanding - - (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta); - [[maybe_unused]] Number const managementFeeReleased = - managementFeeOutstandingProxy - newRoundedLoanState.managementFeeDue; - [[maybe_unused]] Number const interestPart = overpaymentComponents.trackedInterestPart(); + // + // The valueChange returned by tryOverpayment satisfies + // valueChange = (newInterestDue - oldInterestDue) + untrackedInterest. + // Using the loan-state identity v = p + i + m and the adjacent + // `principal change agrees` assertion (dp = oldP - newP), this + // rearranges into three independently-computable terms: + // + // 1. TVO change beyond what principal repayment alone explains: + // newTVO - (oldTVO - dp) + // 2. Management fee released by re-amortization (positive when + // mfee decreased; zero when managementFeeRate == 0): + // oldMfee - newMfee + // 3. The overpayment's penalty interest part (= untrackedInterest + // for the overpayment path; see computeOverpaymentComponents): + // trackedInterestPart() + bool const fix320Enabled = rules.enabled(fixCleanup3_2_0); + XRPL_ASSERT_IF( + fix320Enabled, + overpaymentComponents.trackedPrincipalDelta == + principalOutstandingProxy - newRoundedLoanState.principalOutstanding, + "xrpl::detail::doOverpayment : principal change agrees"); - XRPL_ASSERT_PARTS( - overpaymentComponents.trackedPrincipalDelta == - principalOutstandingProxy - newRoundedLoanState.principalOutstanding, - "xrpl::detail::doOverpayment", - "principal change agrees"); + XRPL_ASSERT_IF( + fix320Enabled, + [&] { + Number const tvoChange = newRoundedLoanState.valueOutstanding - + (totalValueOutstandingProxy - overpaymentComponents.trackedPrincipalDelta); + Number const managementFeeReleased = + managementFeeOutstandingProxy - newRoundedLoanState.managementFeeDue; + Number const interestPart = overpaymentComponents.trackedInterestPart(); + return loanPaymentParts.valueChange == tvoChange + managementFeeReleased + interestPart; + }(), + "xrpl::detail::doOverpayment : interest paid agrees"); - XRPL_ASSERT_PARTS( - loanPaymentParts.valueChange == tvoChange + managementFeeReleased + interestPart, - "xrpl::detail::doOverpayment", - "interest paid agrees"); - - XRPL_ASSERT_PARTS( - overpaymentComponents.trackedPrincipalDelta == loanPaymentParts.principalPaid, - "xrpl::detail::doOverpayment", - "principal payment matches"); - } + XRPL_ASSERT_IF( + fix320Enabled, + overpaymentComponents.trackedPrincipalDelta == loanPaymentParts.principalPaid, + "xrpl::detail::doOverpayment : principal payment matches"); // All validations passed, so update the proxy objects (which will // modify the actual Loan ledger object) @@ -1326,13 +1326,11 @@ computeOverpaymentComponents( TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate) { - if (rules.enabled(fixCleanup3_2_0)) - { - XRPL_ASSERT( - overpayment > 0 && isRounded(asset, overpayment, loanScale), - "xrpl::detail::computeOverpaymentComponents : valid overpayment " - "amount"); - } + XRPL_ASSERT_IF( + rules.enabled(fixCleanup3_2_0), + overpayment > 0 && isRounded(asset, overpayment, loanScale), + "xrpl::detail::computeOverpaymentComponents : valid overpayment " + "amount"); // First, deduct the fixed overpayment fee from the total amount. // This reduces the effective payment that will be applied to the loan. diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index 387116d820..8b3385471d 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -736,10 +736,10 @@ unlockEscrowMPT( STAmount const& grossAmount, beast::Journal j) { - if (!view.rules().enabled(fixTokenEscrowV1)) - { - XRPL_ASSERT(netAmount == grossAmount, "xrpl::unlockEscrowMPT : netAmount == grossAmount"); - } + XRPL_ASSERT_IF( + !view.rules().enabled(fixTokenEscrowV1), + netAmount == grossAmount, + "xrpl::unlockEscrowMPT : netAmount == grossAmount"); auto const& issuer = netAmount.getIssuer(); auto const& mptIssue = netAmount.get(); From fccb109e4829a3608f3b35f97586ccd41452061a Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 9 Jun 2026 18:36:17 +0100 Subject: [PATCH 75/87] feat: Use C++ 23 standard (#7431) --- .clang-tidy | 2 +- .../workflows/reusable-build-test-config.yml | 2 +- BUILD.md | 14 +- CMakeLists.txt | 2 +- conan/lockfile/linux.profile | 2 +- conan/lockfile/macos.profile | 2 +- conan/lockfile/windows.profile | 2 +- conan/profiles/default | 2 +- include/xrpl/basics/Expected.h | 248 ------------------ include/xrpl/basics/base_uint.h | 8 +- include/xrpl/beast/hash/xxhasher.h | 3 + include/xrpl/ledger/helpers/AMMHelpers.h | 9 +- .../xrpl/ledger/helpers/AccountRootHelpers.h | 4 +- include/xrpl/ledger/helpers/LendingHelpers.h | 5 +- include/xrpl/protocol/STTx.h | 16 +- include/xrpl/protocol/XChainAttestations.h | 2 +- include/xrpl/protocol/detail/STVar.h | 2 +- include/xrpl/protocol/tokens.h | 4 +- include/xrpl/tx/SignerEntries.h | 4 +- .../transactors/token/MPTokenIssuanceCreate.h | 5 +- .../xrpl/tx/transactors/vault/VaultClawback.h | 4 +- src/libxrpl/ledger/helpers/AMMHelpers.cpp | 32 +-- .../ledger/helpers/AccountRootHelpers.cpp | 6 +- .../ledger/helpers/CredentialHelpers.cpp | 6 +- src/libxrpl/ledger/helpers/LendingHelpers.cpp | 48 ++-- src/libxrpl/protocol/STTx.cpp | 46 ++-- src/libxrpl/protocol/tokens.cpp | 34 +-- src/libxrpl/tx/SignerEntries.cpp | 8 +- .../tx/transactors/bridge/XChainBridge.cpp | 50 ++-- src/libxrpl/tx/transactors/dex/AMMBid.cpp | 8 +- .../lending/LoanBrokerCoverClawback.cpp | 18 +- .../tx/transactors/lending/LoanPay.cpp | 4 +- .../tx/transactors/nft/NFTokenMint.cpp | 10 +- .../token/MPTokenIssuanceCreate.cpp | 14 +- .../tx/transactors/vault/VaultClawback.cpp | 22 +- src/test/app/Delegate_test.cpp | 4 +- src/test/app/GRPCServerTLS_test.cpp | 26 +- src/test/app/Invariants_test.cpp | 5 +- src/test/app/MultiSign_test.cpp | 4 +- src/test/app/NFTokenBurn_test.cpp | 8 +- src/test/app/NetworkOPs_test.cpp | 2 +- src/test/app/ValidatorSite_test.cpp | 12 +- src/test/basics/Expected_test.cpp | 221 ---------------- src/test/consensus/Consensus_test.cpp | 9 +- src/test/jtx/CheckMessageLogs.h | 2 +- src/test/jtx/directory.h | 4 +- src/test/jtx/impl/directory.cpp | 20 +- src/test/nodestore/NuDBFactory_test.cpp | 18 +- src/test/overlay/reduce_relay_test.cpp | 2 +- src/test/server/ServerStatus_test.cpp | 14 +- src/test/server/Server_test.cpp | 16 +- src/xrpld/overlay/detail/PeerImp.cpp | 2 +- src/xrpld/rpc/detail/RPCLedgerHelpers.cpp | 29 +- src/xrpld/rpc/detail/RPCLedgerHelpers.h | 5 +- src/xrpld/rpc/detail/TransactionSign.cpp | 10 +- src/xrpld/rpc/handlers/ledger/Ledger.cpp | 6 +- src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp | 156 +++++------ .../rpc/handlers/ledger/LedgerEntryHelpers.h | 37 +-- src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp | 28 +- .../server_info/ServerDefinitions.cpp | 2 +- .../rpc/handlers/transaction/Simulate.cpp | 10 +- src/xrpld/rpc/handlers/transaction/Submit.cpp | 6 +- 62 files changed, 412 insertions(+), 894 deletions(-) delete mode 100644 include/xrpl/basics/Expected.h delete mode 100644 src/test/basics/Expected_test.cpp diff --git a/.clang-tidy b/.clang-tidy index 2d72eae701..e09d326916 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -153,7 +153,7 @@ Checks: "-*, readability-use-std-min-max " # --- -# readability-inconsistent-declaration-parameter-name, # in this codebase this check will break a lot of arg names +# readability-inconsistent-declaration-parameter-name, # In this codebase this check will break a lot of arg names # readability-static-accessed-through-instance, # this check is probably unnecessary. It makes the code less readable # --- diff --git a/.github/workflows/reusable-build-test-config.yml b/.github/workflows/reusable-build-test-config.yml index d53cf97a39..8cb5f8c46a 100644 --- a/.github/workflows/reusable-build-test-config.yml +++ b/.github/workflows/reusable-build-test-config.yml @@ -82,7 +82,7 @@ jobs: name: ${{ inputs.config_name }} runs-on: ${{ fromJSON(inputs.runs_on) }} container: ${{ inputs.image != '' && inputs.image || null }} - timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 60 }} + timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 90 }} env: # Use a namespace to keep the objects separate for each configuration. CCACHE_NAMESPACE: ${{ inputs.config_name }} diff --git a/BUILD.md b/BUILD.md index 1d3fc8f774..662ba0d33d 100644 --- a/BUILD.md +++ b/BUILD.md @@ -45,14 +45,14 @@ found here](./docs/build/environment.md). It is possible to build with Conan 1.60+, but the instructions are significantly different, which is why we are not recommending it. -`xrpld` is written in the C++20 dialect and includes the `` header. -The [minimum compiler versions][2] required are: +`xrpld` is written in the C++23 dialect and includes the `` header. +The [tested compiler versions][2] are: | Compiler | Version | | ----------- | --------- | -| GCC | 12 | -| Clang | 16 | -| Apple Clang | 16 | +| GCC | 15 | +| Clang | 22 | +| Apple Clang | 17 | | MSVC | 19.44[^3] | ### Linux @@ -232,11 +232,11 @@ name and then creating a new `default` profile for a different compiler. #### Select language The default profile created by Conan will typically select different C++ dialect -than C++20 used by this project. You should set `20` in the profile line +than C++23 used by this project. You should set `23` in the profile line starting with `compiler.cppstd=`. For example: ```bash -sed -i.bak -e 's|^compiler\.cppstd=.*$|compiler.cppstd=20|' $(conan config home)/profiles/default +sed -i.bak -e 's|^compiler\.cppstd=.*$|compiler.cppstd=23|' $(conan config home)/profiles/default ``` #### Select standard library in Linux diff --git a/CMakeLists.txt b/CMakeLists.txt index d315a5dcec..3dbe60a220 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") project(xrpl) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/conan/lockfile/linux.profile b/conan/lockfile/linux.profile index 25ad5988c5..ea9f66de69 100644 --- a/conan/lockfile/linux.profile +++ b/conan/lockfile/linux.profile @@ -2,7 +2,7 @@ arch=x86_64 build_type=Release compiler=gcc -compiler.cppstd=20 +compiler.cppstd=23 compiler.libcxx=libstdc++11 compiler.version=13 os=Linux diff --git a/conan/lockfile/macos.profile b/conan/lockfile/macos.profile index 332a0c143d..f223627c26 100644 --- a/conan/lockfile/macos.profile +++ b/conan/lockfile/macos.profile @@ -2,7 +2,7 @@ arch=armv8 build_type=Release compiler=apple-clang -compiler.cppstd=20 +compiler.cppstd=23 compiler.libcxx=libc++ compiler.version=17.0 os=Macos diff --git a/conan/lockfile/windows.profile b/conan/lockfile/windows.profile index 4bb266a62e..b3a8fed4f3 100644 --- a/conan/lockfile/windows.profile +++ b/conan/lockfile/windows.profile @@ -2,7 +2,7 @@ arch=x86_64 build_type=Release compiler=msvc -compiler.cppstd=20 +compiler.cppstd=23 compiler.runtime=dynamic compiler.runtime_type=Release compiler.version=194 diff --git a/conan/profiles/default b/conan/profiles/default index cde59f7f3b..e0a88ebca1 100644 --- a/conan/profiles/default +++ b/conan/profiles/default @@ -12,7 +12,7 @@ arch={{ arch }} build_type=Debug compiler={{compiler}} compiler.version={{ compiler_version }} -compiler.cppstd=20 +compiler.cppstd=23 {% if os == "Windows" %} compiler.runtime=static {% else %} diff --git a/include/xrpl/basics/Expected.h b/include/xrpl/basics/Expected.h deleted file mode 100644 index 3796151777..0000000000 --- a/include/xrpl/basics/Expected.h +++ /dev/null @@ -1,248 +0,0 @@ -#pragma once - -#include - -#include - -#include - -namespace xrpl { - -/** Expected is an approximation of std::expected (hoped for in C++23) - - See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html - - The implementation is entirely based on boost::outcome_v2::result. -*/ - -// Exception thrown by an invalid access to Expected. -struct BadExpectedAccess : public std::runtime_error -{ - BadExpectedAccess() : runtime_error("bad expected access") - { - } -}; - -namespace detail { - -// Custom policy for Expected. Always throw on an invalid access. -struct ThrowPolicy : public boost::outcome_v2::policy::base -{ - template - static constexpr void - // NOLINTNEXTLINE(readability-identifier-naming) - wide_value_check(Impl&& self) - { - if (!base::_has_value(std::forward(self))) - Throw(); - } - - template - static constexpr void - // NOLINTNEXTLINE(readability-identifier-naming) - wide_error_check(Impl&& self) - { - if (!base::_has_error(std::forward(self))) - Throw(); - } - - template - static constexpr void - // NOLINTNEXTLINE(readability-identifier-naming) - wide_exception_check(Impl&& self) - { - if (!base::_has_exception(std::forward(self))) - Throw(); - } -}; - -} // namespace detail - -// Definition of Unexpected, which is used to construct the unexpected -// return type of an Expected. -template -class Unexpected -{ -public: - static_assert(!std::is_same_v, "E must not be void"); - - Unexpected() = delete; - - constexpr explicit Unexpected(E const& e) : val_(e) - { - } - - constexpr explicit Unexpected(E&& e) : val_(std::move(e)) - { - } - - [[nodiscard]] constexpr E const& - value() const& - { - return val_; - } - - constexpr E& - value() & - { - return val_; - } - - constexpr E&& - value() && - { - return std::move(val_); - } - - [[nodiscard]] constexpr E const&& - value() const&& - { - return std::move(val_); - } - -private: - E val_; -}; - -// Unexpected deduction guide that converts array to const*. -template -Unexpected(E (&)[N]) -> Unexpected; - -// Definition of Expected. All of the machinery comes from boost::result. -template -class [[nodiscard]] Expected : private boost::outcome_v2::result -{ - using Base = boost::outcome_v2::result; - -public: - template - requires std::convertible_to - constexpr Expected(U&& r) : Base(boost::outcome_v2::in_place_type_t{}, std::forward(r)) - { - } - - template - requires std::convertible_to && (!std::is_reference_v) - constexpr Expected(Unexpected e) - : Base(boost::outcome_v2::in_place_type_t{}, std::move(e.value())) - { - } - - [[nodiscard]] constexpr bool - // NOLINTNEXTLINE(readability-identifier-naming) - has_value() const - { - return Base::has_value(); - } - - [[nodiscard]] constexpr T const& - value() const - { - return Base::value(); - } - - constexpr T& - value() - { - return Base::value(); - } - - [[nodiscard]] constexpr E const& - error() const& - { - return Base::error(); - } - - [[nodiscard]] constexpr E& - error() & - { - return Base::error(); - } - - [[nodiscard]] constexpr E&& - error() && - { - return std::move(Base::error()); - } - - constexpr explicit - operator bool() const - { - return has_value(); - } - - // Add operator* and operator-> so the Expected API looks a bit more like - // what std::expected is likely to look like. See: - // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html - [[nodiscard]] constexpr T& - operator*() - { - return this->value(); - } - - [[nodiscard]] constexpr T const& - operator*() const - { - return this->value(); - } - - [[nodiscard]] constexpr T* - operator->() - { - return &this->value(); - } - - [[nodiscard]] constexpr T const* - operator->() const - { - return &this->value(); - } -}; - -// Specialization of Expected. Allows returning either success -// (without a value) or the reason for the failure. -template -class [[nodiscard]] -Expected : private boost::outcome_v2::result -{ - using Base = boost::outcome_v2::result; - -public: - // The default constructor makes a successful Expected. - // This aligns with std::expected behavior proposed in P0323R10. - constexpr Expected() : Base(boost::outcome_v2::success()) - { - } - - template - requires std::convertible_to && (!std::is_reference_v) - constexpr Expected(Unexpected e) : Base(E(std::move(e.value()))) - { - } - - [[nodiscard]] constexpr E const& - error() const& - { - return Base::error(); - } - - [[nodiscard]] constexpr E& - error() & - { - return Base::error(); - } - - [[nodiscard]] constexpr E&& - error() && - { - return std::move(Base::error()); - } - - constexpr explicit - operator bool() const - { - return Base::has_value(); - } -}; - -} // namespace xrpl diff --git a/include/xrpl/basics/base_uint.h b/include/xrpl/basics/base_uint.h index 93a9ced15e..93520ff699 100644 --- a/include/xrpl/basics/base_uint.h +++ b/include/xrpl/basics/base_uint.h @@ -5,7 +5,6 @@ #pragma once -#include #include #include #include @@ -20,6 +19,7 @@ #include #include #include +#include #include namespace xrpl { @@ -177,7 +177,7 @@ private: BadChar, }; - constexpr Expected + constexpr std::expected parseFromStringView(std::string_view sv) noexcept { // Local lambda that converts a single hex char to four bits and @@ -216,7 +216,7 @@ private: } if (sv.size() != size() * 2) - return Unexpected(ParseResult::BadLength); + return std::unexpected(ParseResult::BadLength); std::size_t i = 0u; auto in = sv.begin(); @@ -227,7 +227,7 @@ private: { if (auto const result = hexCharToUInt(*in++, shift, accum); result != ParseResult::Okay) - return Unexpected(result); + return std::unexpected(result); } ret[i++] = accum; } diff --git a/include/xrpl/beast/hash/xxhasher.h b/include/xrpl/beast/hash/xxhasher.h index 95a67dede0..978bbc6917 100644 --- a/include/xrpl/beast/hash/xxhasher.h +++ b/include/xrpl/beast/hash/xxhasher.h @@ -7,8 +7,11 @@ #include #include #include +#include +#include #include #include +#include namespace beast { diff --git a/include/xrpl/ledger/helpers/AMMHelpers.h b/include/xrpl/ledger/helpers/AMMHelpers.h index 61d6e9d2fb..d21e50e7cb 100644 --- a/include/xrpl/ledger/helpers/AMMHelpers.h +++ b/include/xrpl/ledger/helpers/AMMHelpers.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -18,6 +17,8 @@ #include #include +#include + namespace xrpl { namespace detail { @@ -741,7 +742,7 @@ ammPoolHolds( * provided then they are used as the AMM token pair issues. * Otherwise the missing issues are fetched from ammSle. */ -Expected, TER> +std::expected, TER> ammHolds( ReadView const& view, SLE const& ammSle, @@ -801,14 +802,14 @@ initializeFeeAuctionVote( * otherwise. Return tecINTERNAL if encountered an unexpected condition, * for instance Liquidity Provider has more than one LPToken trustline. */ -Expected +std::expected isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount); /** 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. */ -Expected +std::expected verifyAndAdjustLPTokenBalance( Sandbox& sb, STAmount const& lpTokens, diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index c02cad98d8..cf6082d533 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -9,6 +8,7 @@ #include #include +#include #include #include @@ -91,7 +91,7 @@ isPseudoAccount( * before using a field. The amendment check is **not** performed in * createPseudoAccount. */ -[[nodiscard]] Expected +[[nodiscard]] std::expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField); /** Checks the destination and tag. diff --git a/include/xrpl/ledger/helpers/LendingHelpers.h b/include/xrpl/ledger/helpers/LendingHelpers.h index 32f94ee277..8de945233b 100644 --- a/include/xrpl/ledger/helpers/LendingHelpers.h +++ b/include/xrpl/ledger/helpers/LendingHelpers.h @@ -4,6 +4,7 @@ #include #include +#include #include namespace xrpl { @@ -397,7 +398,7 @@ struct LoanStateDeltas nonNegative(); }; -Expected, TER> +std::expected, TER> tryOverpayment( Rules const& rules, Asset const& asset, @@ -523,7 +524,7 @@ isRounded(Asset const& asset, Number const& value, std::int32_t scale); // potential extra work at the end. enum class LoanPaymentType { Regular = 0, Late, Full, Overpayment }; -Expected +std::expected loanMakePayment( Asset const& asset, ApplyView& view, diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index 4deedfafb7..659fede31d 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -11,6 +10,7 @@ #include +#include #include namespace xrpl { @@ -108,10 +108,10 @@ public: @param rules The current ledger rules. @return `true` if valid signature. If invalid, the error message string. */ - Expected + std::expected checkSign(Rules const& rules) const; - Expected + std::expected checkBatchSign(Rules const& rules) const; // SQL Functions with metadata. @@ -138,19 +138,19 @@ private: Will be *this more often than not. @return `true` if valid signature. If invalid, the error message string. */ - Expected + std::expected checkSign(Rules const& rules, STObject const& sigObject) const; - Expected + std::expected checkSingleSign(STObject const& sigObject) const; - Expected + std::expected checkMultiSign(Rules const& rules, STObject const& sigObject) const; - Expected + std::expected checkBatchSingleSign(STObject const& batchSigner) const; - Expected + std::expected checkBatchMultiSign(STObject const& batchSigner, Rules const& rules) const; STBase* diff --git a/include/xrpl/protocol/XChainAttestations.h b/include/xrpl/protocol/XChainAttestations.h index 993f478b5e..457af727a2 100644 --- a/include/xrpl/protocol/XChainAttestations.h +++ b/include/xrpl/protocol/XChainAttestations.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -15,6 +14,7 @@ #include #include +#include #include #include diff --git a/include/xrpl/protocol/detail/STVar.h b/include/xrpl/protocol/detail/STVar.h index 98a0b8dcd2..71077d4b33 100644 --- a/include/xrpl/protocol/detail/STVar.h +++ b/include/xrpl/protocol/detail/STVar.h @@ -37,7 +37,7 @@ private: // The largest "small object" we can accommodate static constexpr std::size_t kMaxSize = 72; - std::aligned_storage::type d_ = {}; + alignas(std::max_align_t) std::byte d_[kMaxSize] = {}; STBase* p_ = nullptr; public: diff --git a/include/xrpl/protocol/tokens.h b/include/xrpl/protocol/tokens.h index 125cfe8583..67cb25c7fb 100644 --- a/include/xrpl/protocol/tokens.h +++ b/include/xrpl/protocol/tokens.h @@ -1,10 +1,10 @@ #pragma once -#include #include #include #include +#include #include #include #include @@ -13,7 +13,7 @@ namespace xrpl { template -using B58Result = Expected; +using B58Result = std::expected; enum class TokenType : std::uint8_t { None = 1, // unused diff --git a/include/xrpl/tx/SignerEntries.h b/include/xrpl/tx/SignerEntries.h index 91fc4bd030..af0c9b9d28 100644 --- a/include/xrpl/tx/SignerEntries.h +++ b/include/xrpl/tx/SignerEntries.h @@ -1,11 +1,11 @@ #pragma once -#include // #include // beast::Journal #include // temMALFORMED #include // AccountID #include // NotTEC +#include #include #include @@ -60,7 +60,7 @@ public: // obj Contains a SignerEntries field that is an STArray. // journal For reporting error conditions. // annotation Source of SignerEntries, like "ledger" or "transaction". - static Expected, NotTEC> + static std::expected, NotTEC> deserialize(STObject const& obj, beast::Journal journal, std::string_view annotation); }; diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h index a706c71e18..d946587e32 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h @@ -1,9 +1,10 @@ #pragma once -#include #include #include +#include + namespace xrpl { // NOLINTBEGIN(readability-redundant-member-init) @@ -61,7 +62,7 @@ public: ReadView const& view, beast::Journal const& j) override; - static Expected + static std::expected create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args); }; diff --git a/include/xrpl/tx/transactors/vault/VaultClawback.h b/include/xrpl/tx/transactors/vault/VaultClawback.h index b8032809ee..2ff97abca2 100644 --- a/include/xrpl/tx/transactors/vault/VaultClawback.h +++ b/include/xrpl/tx/transactors/vault/VaultClawback.h @@ -2,6 +2,8 @@ #include +#include + namespace xrpl { class VaultClawback : public Transactor @@ -34,7 +36,7 @@ public: beast::Journal const& j) override; private: - Expected, TER> + std::expected, TER> assetsToClawback( SLE::ref vault, SLE::const_ref sleShareIssuance, diff --git a/src/libxrpl/ledger/helpers/AMMHelpers.cpp b/src/libxrpl/ledger/helpers/AMMHelpers.cpp index fe6d022490..a59b8e4436 100644 --- a/src/libxrpl/ledger/helpers/AMMHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AMMHelpers.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -34,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -433,7 +433,7 @@ ammPoolHolds( return std::make_pair(assetInBalance, assetOutBalance); } -Expected, TER> +std::expected, TER> ammHolds( ReadView const& view, SLE const& ammSle, @@ -489,7 +489,7 @@ ammHolds( return std::make_optional(std::make_pair(asset1, asset2)); }(); if (!assets) - return Unexpected(tecAMM_INVALID_TOKENS); + return std::unexpected(tecAMM_INVALID_TOKENS); auto const [amount1, amount2] = ammPoolHolds( view, ammSle.getAccountID(sfAccount), @@ -821,7 +821,7 @@ initializeFeeAuctionVote( auctionSlot.makeFieldAbsent(sfAuthAccounts); } -Expected +std::expected isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount) { // Liquidity Provider (LP) must have one LPToken trustline @@ -852,18 +852,18 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c { auto const ownerDir = view.read(currentIndex); if (!ownerDir) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE for (auto const& key : ownerDir->getFieldV256(sfIndexes)) { auto const sle = view.read(keylet::child(key)); if (!sle) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE auto const entryType = sle->getFieldU16(sfLedgerEntryType); // Only one AMM object if (entryType == ltAMM) { if (hasAMM) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE hasAMM = true; continue; } @@ -873,7 +873,7 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c continue; } if (entryType != ltRIPPLE_STATE) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE auto const lowLimit = sle->getFieldAmount(sfLowLimit); auto const highLimit = sle->getFieldAmount(sfHighLimit); auto const isLPTrustline = @@ -889,12 +889,12 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c { // LP has exactly one LPToken trustline if (++nLPTokenTrustLines > 1) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE } // AMM account has at most two IOU trustlines else if (++nIOUTrustLines > 2) { - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE } } // Another Liquidity Provider LPToken trustline @@ -905,7 +905,7 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c // AMM account has at most two IOU trustlines else if (++nIOUTrustLines > 2) { - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE } } auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext); @@ -913,15 +913,15 @@ isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID c { if (nLPTokenTrustLines != 1 || (nIOUTrustLines == 0 && nMPT == 0) || (nIOUTrustLines > 2 || nMPT > 2) || (nIOUTrustLines + nMPT) > 2) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE return true; } currentIndex = keylet::page(root, uNodeNext); } - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE } -Expected +std::expected verifyAndAdjustLPTokenBalance( Sandbox& sb, STAmount const& lpTokens, @@ -931,7 +931,7 @@ verifyAndAdjustLPTokenBalance( auto const res = isOnlyLiquidityProvider(sb, lpTokens.get(), account); if (!res.has_value()) { - return Unexpected(res.error()); + return std::unexpected(res.error()); } if (res.value()) @@ -944,7 +944,7 @@ verifyAndAdjustLPTokenBalance( } else { - return Unexpected(tecAMM_INVALID_TOKENS); + return std::unexpected(tecAMM_INVALID_TOKENS); } } return true; diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 1634de93c9..1c4acc7bc4 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -22,6 +21,7 @@ #include #include +#include #include #include #include @@ -202,7 +202,7 @@ isPseudoAccount(SLE::const_pointer sleAcct, std::set const& pseud }) > 0; } -Expected +std::expected createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const& ownerField) { [[maybe_unused]] @@ -216,7 +216,7 @@ createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey); if (accountId == beast::kZero) - return Unexpected(tecDUPLICATE); + return std::unexpected(tecDUPLICATE); // Create pseudo-account. auto account = std::make_shared(keylet::account(accountId)); diff --git a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp index 28b50b51d6..ca5876f88a 100644 --- a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -24,6 +23,7 @@ #include #include +#include #include #include #include @@ -43,7 +43,7 @@ checkExpired(SLE const& sleCredential, NetClock::time_point const& closed) } [[nodiscard]] -static Expected +static std::expected removeExpired(ApplyView& view, STVector256 const& arr, beast::Journal const j) { auto const closeTime = view.header().parentCloseTime; @@ -61,7 +61,7 @@ removeExpired(ApplyView& view, STVector256 const& arr, beast::Journal const j) // delete expired credentials even if the transaction failed auto const err = deleteSLE(view, sleCred, j); if (view.rules().enabled(fixCleanup3_1_3) && !isTesSuccess(err)) - return Unexpected(err); + return std::unexpected(err); foundExpired = true; } } diff --git a/src/libxrpl/ledger/helpers/LendingHelpers.cpp b/src/libxrpl/ledger/helpers/LendingHelpers.cpp index 0a195f9cbe..676b473132 100644 --- a/src/libxrpl/ledger/helpers/LendingHelpers.cpp +++ b/src/libxrpl/ledger/helpers/LendingHelpers.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -25,6 +24,7 @@ #include #include #include +#include #include #include @@ -514,7 +514,7 @@ doPayment( * The function preserves accumulated rounding errors across the re-amortization * to ensure the loan state remains consistent with its payment history. */ -Expected, TER> +std::expected, TER> tryOverpayment( Rules const& rules, Asset const& asset, @@ -643,7 +643,7 @@ tryOverpayment( JLOG(j.warn()) << "Principal overpayment would cause the loan to be in " "an invalid state. Ignore the overpayment"; - return Unexpected(tesSUCCESS); + return std::unexpected(tesSUCCESS); } // Validate that all computed properties are reasonable. These checks should @@ -660,7 +660,7 @@ tryOverpayment( << ", PeriodicPayment : " << newLoanProperties.periodicPayment << ", ManagementFeeOwedToBroker: " << newLoanProperties.loanState.managementFeeDue; - return Unexpected(tesSUCCESS); + return std::unexpected(tesSUCCESS); // LCOV_EXCL_STOP } @@ -685,7 +685,7 @@ tryOverpayment( { JLOG(j.warn()) << "Principal overpayment would increase the value of " "the loan. Ignore the overpayment"; - return Unexpected(tesSUCCESS); + return std::unexpected(tesSUCCESS); } return std::make_pair( @@ -718,7 +718,7 @@ tryOverpayment( * gracefully without corrupting the ledger data. */ template -Expected +std::expected doOverpayment( Rules const& rules, Asset const& asset, @@ -760,7 +760,7 @@ doOverpayment( managementFeeRate, j); if (!ret) - return Unexpected(ret.error()); + return std::unexpected(ret.error()); auto const& [loanPaymentParts, newLoanProperties] = *ret; auto const newRoundedLoanState = newLoanProperties.loanState; @@ -774,7 +774,7 @@ doOverpayment( JLOG(j.warn()) << "Overpayment not allowed: principal " << "outstanding did not decrease. Before: " << *principalOutstandingProxy << ". After: " << newRoundedLoanState.principalOutstanding; - return Unexpected(tesSUCCESS); + return std::unexpected(tesSUCCESS); // LCOV_EXCL_STOP } @@ -860,7 +860,7 @@ doOverpayment( * * Implements equation (15) from XLS-66 spec, Section A-2 Equation Glossary */ -Expected +std::expected computeLatePayment( Asset const& asset, ApplyView const& view, @@ -877,7 +877,7 @@ computeLatePayment( // Check if the due date has passed. If not, reject the payment as // being too soon if (!hasExpired(view, nextDueDate)) - return Unexpected(tecTOO_SOON); + return std::unexpected(tecTOO_SOON); // Calculate the penalty interest based on how long the payment is overdue. auto const latePaymentInterest = loanLatePaymentInterest( @@ -929,7 +929,7 @@ computeLatePayment( { JLOG(j.warn()) << "Late loan payment amount is insufficient. Due: " << late.totalDue << ", paid: " << amount; - return Unexpected(tecINSUFFICIENT_PAYMENT); + return std::unexpected(tecINSUFFICIENT_PAYMENT); } return late; @@ -954,7 +954,7 @@ computeLatePayment( * * Implements equation (26) from XLS-66 spec, Section A-2 Equation Glossary */ -Expected +std::expected computeFullPayment( Asset const& asset, ApplyView& view, @@ -979,7 +979,7 @@ computeFullPayment( { // If this is the last payment, it has to be a regular payment JLOG(j.warn()) << "Last payment cannot be a full payment."; - return Unexpected(tecKILLED); + return std::unexpected(tecKILLED); } // Calculate the theoretical principal based on the payment schedule. @@ -1059,7 +1059,7 @@ computeFullPayment( { // If the payment is less than the full payment amount, it's not // sufficient to be a full payment. - return Unexpected(tecINSUFFICIENT_PAYMENT); + return std::unexpected(tecINSUFFICIENT_PAYMENT); } return full; @@ -1780,7 +1780,7 @@ computeLoanProperties( * It is an implementation of the make_payment function from the XLS-66 * spec. Section 3.2.4.4 */ -Expected +std::expected loanMakePayment( Asset const& asset, ApplyView& view, @@ -1800,7 +1800,7 @@ loanMakePayment( // Loan complete this is already checked in LoanPay::preclaim() // LCOV_EXCL_START JLOG(j.warn()) << "Loan is already paid off."; - return Unexpected(tecKILLED); + return std::unexpected(tecKILLED); // LCOV_EXCL_STOP } @@ -1812,7 +1812,7 @@ loanMakePayment( if (*nextDueDateProxy == 0) { JLOG(j.warn()) << "Loan next payment due date is not set."; - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); } std::int32_t const loanScale = loan->at(sfLoanScale); @@ -1850,7 +1850,7 @@ loanMakePayment( << startDate << ", prev payment due date is " << prevPaymentDateProxy << ", next payment due date is " << nextDueDateProxy << ", ledger time is " << view.parentCloseTime().time_since_epoch().count(); - return Unexpected(tecEXPIRED); + return std::unexpected(tecEXPIRED); } // ------------------------------------------------------------- @@ -1900,13 +1900,13 @@ loanMakePayment( // error() will be the TER returned if a payment is not made. It // will only evaluate to true if it's unsuccessful. Otherwise, // tesSUCCESS means nothing was done, so continue. - return Unexpected(fullPaymentComponents.error()); + return std::unexpected(fullPaymentComponents.error()); } // LCOV_EXCL_START UNREACHABLE("xrpl::loanMakePayment : invalid full payment result"); JLOG(j.error()) << "Full payment computation failed unexpectedly."; - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); // LCOV_EXCL_STOP } @@ -1968,13 +1968,13 @@ loanMakePayment( { // error() will be the TER returned if a payment is not made. It // will only evaluate to true if it's unsuccessful. - return Unexpected(latePaymentComponents.error()); + return std::unexpected(latePaymentComponents.error()); } // LCOV_EXCL_START UNREACHABLE("xrpl::loanMakePayment : invalid late payment result"); JLOG(j.error()) << "Late payment computation failed unexpectedly."; - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); // LCOV_EXCL_STOP } @@ -2041,7 +2041,7 @@ loanMakePayment( { JLOG(j.warn()) << "Regular loan payment amount is insufficient. Due: " << periodic.totalDue << ", paid: " << amount; - return Unexpected(tecINSUFFICIENT_PAYMENT); + return std::unexpected(tecINSUFFICIENT_PAYMENT); } XRPL_ASSERT_PARTS( @@ -2127,7 +2127,7 @@ loanMakePayment( // made. It will only evaluate to true if it's unsuccessful. // Otherwise, tesSUCCESS means nothing was done, so // continue. - return Unexpected(overResult.error()); + return std::unexpected(overResult.error()); } } } diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index 2777981fd7..55f0ea1289 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include @@ -40,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -248,7 +248,7 @@ STTx::sign( tid_ = getHash(HashPrefix::TransactionId); } -Expected +std::expected STTx::checkSign(Rules const& rules, STObject const& sigObject) const { try @@ -263,11 +263,11 @@ STTx::checkSign(Rules const& rules, STObject const& sigObject) const } catch (...) { - return Unexpected("Internal signature check failure."); + return std::unexpected("Internal signature check failure."); } } -Expected +std::expected STTx::checkSign(Rules const& rules) const { if (auto const ret = checkSign(rules, *this); !ret) @@ -277,12 +277,12 @@ STTx::checkSign(Rules const& rules) const { auto const counterSig = getFieldObject(sfCounterpartySignature); if (auto const ret = checkSign(rules, counterSig); !ret) - return Unexpected("Counterparty: " + ret.error()); + return std::unexpected("Counterparty: " + ret.error()); } return {}; } -Expected +std::expected STTx::checkBatchSign(Rules const& rules) const { try @@ -291,7 +291,7 @@ STTx::checkBatchSign(Rules const& rules) const if (getTxnType() != ttBATCH) { JLOG(debugLog().fatal()) << "not a batch transaction"; - return Unexpected("Not a batch transaction."); + return std::unexpected("Not a batch transaction."); } STArray const& signers{getFieldArray(sfBatchSigners)}; for (auto const& signer : signers) @@ -309,7 +309,7 @@ STTx::checkBatchSign(Rules const& rules) const { JLOG(debugLog().error()) << "Batch signature check failed: " << e.what(); } - return Unexpected("Internal batch signature check failure."); + return std::unexpected("Internal batch signature check failure."); } json::Value @@ -389,14 +389,14 @@ STTx::getMetaSQL( safeCast(status) % rTxn % escapedMetaData); } -static Expected +static std::expected singleSignHelper(STObject const& sigObject, Slice const& data) { // We don't allow both a non-empty sfSigningPubKey and an sfSigners. // That would allow the transaction to be signed two ways. So if both // fields are present the signature is invalid. if (sigObject.isFieldPresent(sfSigners)) - return Unexpected("Cannot both single- and multi-sign."); + return std::unexpected("Cannot both single- and multi-sign."); bool validSig = false; try @@ -414,19 +414,19 @@ singleSignHelper(STObject const& sigObject, Slice const& data) } if (!validSig) - return Unexpected("Invalid signature."); + return std::unexpected("Invalid signature."); return {}; } -Expected +std::expected STTx::checkSingleSign(STObject const& sigObject) const { auto const data = getSigningData(*this); return singleSignHelper(sigObject, makeSlice(data)); } -Expected +std::expected STTx::checkBatchSingleSign(STObject const& batchSigner) const { Serializer msg; @@ -434,7 +434,7 @@ STTx::checkBatchSingleSign(STObject const& batchSigner) const return singleSignHelper(batchSigner, msg.slice()); } -Expected +std::expected multiSignHelper( STObject const& sigObject, std::optional txnAccountID, @@ -444,18 +444,18 @@ multiSignHelper( // Make sure the MultiSigners are present. Otherwise they are not // attempting multi-signing and we just have a bad SigningPubKey. if (!sigObject.isFieldPresent(sfSigners)) - return Unexpected("Empty SigningPubKey."); + return std::unexpected("Empty SigningPubKey."); // We don't allow both an sfSigners and an sfTxnSignature. Both fields // being present would indicate that the transaction is signed both ways. if (sigObject.isFieldPresent(sfTxnSignature)) - return Unexpected("Cannot both single- and multi-sign."); + return std::unexpected("Cannot both single- and multi-sign."); STArray const& signers{sigObject.getFieldArray(sfSigners)}; // There are well known bounds that the number of signers must be within. if (signers.size() < STTx::kMinMultiSigners || signers.size() > STTx::kMaxMultiSigners) - return Unexpected("Invalid Signers array size."); + return std::unexpected("Invalid Signers array size."); // Signers must be in sorted order by AccountID. AccountID lastAccountID(beast::kZero); @@ -468,15 +468,15 @@ multiSignHelper( // If they can, txnAccountID will be unseated, which is not equal to any // value. if (txnAccountID == accountID) - return Unexpected("Invalid multisigner."); + return std::unexpected("Invalid multisigner."); // No duplicate signers allowed. if (lastAccountID == accountID) - return Unexpected("Duplicate Signers not allowed."); + return std::unexpected("Duplicate Signers not allowed."); // Accounts must be in order by account ID. No duplicates allowed. if (lastAccountID > accountID) - return Unexpected("Unsorted Signers array."); + return std::unexpected("Unsorted Signers array."); // The next signature must be greater than this one. lastAccountID = accountID; @@ -502,7 +502,7 @@ multiSignHelper( } if (!validSig) { - return Unexpected( + return std::unexpected( std::string("Invalid signature on account ") + toBase58(accountID) + errorWhat.value_or("") + "."); } @@ -511,7 +511,7 @@ multiSignHelper( return {}; } -Expected +std::expected STTx::checkBatchMultiSign(STObject const& batchSigner, Rules const& rules) const { // We can ease the computational load inside the loop a bit by @@ -530,7 +530,7 @@ STTx::checkBatchMultiSign(STObject const& batchSigner, Rules const& rules) const rules); } -Expected +std::expected STTx::checkMultiSign(Rules const& rules, STObject const& sigObject) const { // Used inside the loop in multiSignHelper to enforce that diff --git a/src/libxrpl/protocol/tokens.cpp b/src/libxrpl/protocol/tokens.cpp index a43cbd9c85..fcd822a747 100644 --- a/src/libxrpl/protocol/tokens.cpp +++ b/src/libxrpl/protocol/tokens.cpp @@ -9,7 +9,6 @@ #include -#include #include #include #include @@ -23,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -353,7 +353,7 @@ b256ToB58Be(std::span input, std::span out) // (33 bytes for nodepublic + 1 byte token + 4 bytes checksum) if (input.size() > 38) { - return Unexpected(TokenCodecErrc::InputTooLarge); + return std::unexpected(TokenCodecErrc::InputTooLarge); }; auto countLeadingZeros = [](std::span const& col) -> std::size_t { @@ -441,7 +441,7 @@ b256ToB58Be(std::span input, std::span out) static constexpr std::uint64_t kB5810 = 430804206899405824; // 58^10; if (base5810Coeff[i] >= kB5810) { - return Unexpected(TokenCodecErrc::InputTooLarge); + return std::unexpected(TokenCodecErrc::InputTooLarge); } std::array const b58Be = xrpl::b58_fast::detail::b5810ToB58Be(base5810Coeff[i]); @@ -453,7 +453,7 @@ b256ToB58Be(std::span input, std::span out) skipZeros = false; if (out.size() < ((i + 1) * 10) - toSkip) { - return Unexpected(TokenCodecErrc::OutputTooSmall); + return std::unexpected(TokenCodecErrc::OutputTooSmall); } } for (auto b58Coeff : b58BeS.subspan(toSkip)) @@ -476,11 +476,11 @@ b58ToB256Be(std::string_view input, std::span out) // log(2^(38*8),58) ~= 51.9 if (input.size() > 52) { - return Unexpected(TokenCodecErrc::InputTooLarge); + return std::unexpected(TokenCodecErrc::InputTooLarge); }; if (out.size() < 8) { - return Unexpected(TokenCodecErrc::OutputTooSmall); + return std::unexpected(TokenCodecErrc::OutputTooSmall); } auto countLeadingZeros = [&](auto const& col) -> std::size_t { @@ -513,7 +513,7 @@ b58ToB256Be(std::string_view input, std::span out) auto curVal = ::xrpl::kAlphabetReverse[c]; if (curVal < 0) { - return Unexpected(TokenCodecErrc::InvalidEncodingChar); + return std::unexpected(TokenCodecErrc::InvalidEncodingChar); } b5810Coeff[0] *= 58; b5810Coeff[0] += curVal; @@ -526,7 +526,7 @@ b58ToB256Be(std::string_view input, std::span out) auto curVal = ::xrpl::kAlphabetReverse[c]; if (curVal < 0) { - return Unexpected(TokenCodecErrc::InvalidEncodingChar); + return std::unexpected(TokenCodecErrc::InvalidEncodingChar); } b5810Coeff[numPartialCoeffs + j] *= 58; b5810Coeff[numPartialCoeffs + j] += curVal; @@ -548,7 +548,7 @@ b58ToB256Be(std::string_view input, std::span out) std::span(&result[0], curResultSize + 1), kB5810); if (code != TokenCodecErrc::Success) { - return Unexpected(code); + return std::unexpected(code); } } { @@ -556,7 +556,7 @@ b58ToB256Be(std::string_view input, std::span out) std::span(&result[0], curResultSize + 1), c); if (code != TokenCodecErrc::Success) { - return Unexpected(code); + return std::unexpected(code); } } if (result[curResultSize] != 0) @@ -589,7 +589,7 @@ b58ToB256Be(std::string_view input, std::span out) } if ((curOutI + (8 * (curResultSize - 1))) > out.size()) { - return Unexpected(TokenCodecErrc::OutputTooSmall); + return std::unexpected(TokenCodecErrc::OutputTooSmall); } for (int i = curResultSize - 2; i >= 0; --i) @@ -614,11 +614,11 @@ encodeBase58Token( std::array buf{}; if (input.size() > kTmpBufSize - 5) { - return Unexpected(TokenCodecErrc::InputTooLarge); + return std::unexpected(TokenCodecErrc::InputTooLarge); } if (input.empty()) { - return Unexpected(TokenCodecErrc::InputTooSmall); + return std::unexpected(TokenCodecErrc::InputTooSmall); } // buf[0] = static_cast(tokenType); @@ -648,23 +648,23 @@ decodeBase58Token(TokenType type, std::string_view s, std::span ou // Reject zero length tokens if (ret.size() < 6) - return Unexpected(TokenCodecErrc::InputTooSmall); + return std::unexpected(TokenCodecErrc::InputTooSmall); // The type must match. if (type != static_cast(static_cast(ret[0]))) - return Unexpected(TokenCodecErrc::MismatchedTokenType); + return std::unexpected(TokenCodecErrc::MismatchedTokenType); // And the checksum must as well. std::array guard{}; checksum(guard.data(), ret.data(), ret.size() - guard.size()); if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin())) { - return Unexpected(TokenCodecErrc::MismatchedChecksum); + return std::unexpected(TokenCodecErrc::MismatchedChecksum); } std::size_t const outSize = ret.size() - 1 - guard.size(); if (outBuf.size() < outSize) - return Unexpected(TokenCodecErrc::OutputTooSmall); + return std::unexpected(TokenCodecErrc::OutputTooSmall); // Skip the leading type byte and the trailing checksum. std::copy(ret.begin() + 1, ret.begin() + outSize + 1, outBuf.begin()); return outBuf.subspan(0, outSize); diff --git a/src/libxrpl/tx/SignerEntries.cpp b/src/libxrpl/tx/SignerEntries.cpp index 7251b8260f..943b66292e 100644 --- a/src/libxrpl/tx/SignerEntries.cpp +++ b/src/libxrpl/tx/SignerEntries.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -12,19 +11,20 @@ #include #include +#include #include #include #include namespace xrpl { -Expected, NotTEC> +std::expected, NotTEC> SignerEntries::deserialize(STObject const& obj, beast::Journal journal, std::string_view annotation) { if (!obj.isFieldPresent(sfSignerEntries)) { JLOG(journal.trace()) << "Malformed " << annotation << ": Need signer entry array."; - return Unexpected(temMALFORMED); + return std::unexpected(temMALFORMED); } std::vector accountVec; @@ -37,7 +37,7 @@ SignerEntries::deserialize(STObject const& obj, beast::Journal journal, std::str if (sEntry.getFName() != sfSignerEntry) { JLOG(journal.trace()) << "Malformed " << annotation << ": Expected SignerEntry."; - return Unexpected(temMALFORMED); + return std::unexpected(temMALFORMED); } // Extract SignerEntry fields. diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 7c9c9d95dc..273beaea0f 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -40,6 +39,7 @@ #include #include +#include #include #include #include @@ -186,7 +186,7 @@ checkAttestationPublicKey( enum class CheckDst { Check, Ignore }; template -Expected, TER> +std::expected, TER> claimHelper( XChainAttestationsBase& attestations, ReadView const& view, @@ -233,7 +233,7 @@ claimHelper( if (weight >= quorum) return rewardAccounts; - return Unexpected(tecXCHAIN_CLAIM_NO_QUORUM); + return std::unexpected(tecXCHAIN_CLAIM_NO_QUORUM); } /** @@ -337,7 +337,7 @@ onNewAttestations( // Check if there is a quorum of attestations for the given amount and // chain. If so return the reward accounts, if not return the tec code (most // likely tecXCHAIN_CLAIM_NO_QUORUM) -Expected, TER> +std::expected, TER> onClaim( XChainClaimAttestations& attestations, ReadView const& view, @@ -847,14 +847,14 @@ applyClaimAttestations( AccountID cidOwner; }; - auto const scopeResult = [&]() -> Expected { + auto const scopeResult = [&]() -> std::expected { // This lambda is ugly - admittedly. The purpose of this lambda is to // limit the scope of sles so they don't overlap with // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child // views, it's important that the sle's lifetime doesn't overlap. auto const sleClaimID = psb.peek(claimIDKeylet); if (!sleClaimID) - return Unexpected(tecXCHAIN_NO_CLAIM_ID); + return std::unexpected(tecXCHAIN_NO_CLAIM_ID); // Add claims that are part of the signer's list to the "claims" vector std::vector atts; @@ -868,13 +868,13 @@ applyClaimAttestations( if (atts.empty()) { - return Unexpected(tecXCHAIN_PROOF_UNKNOWN_KEY); + return std::unexpected(tecXCHAIN_PROOF_UNKNOWN_KEY); } AccountID const otherChainSource = (*sleClaimID)[sfOtherChainSource]; if (attBegin->sendingAccount != otherChainSource) { - return Unexpected(tecXCHAIN_SENDING_ACCOUNT_MISMATCH); + return std::unexpected(tecXCHAIN_SENDING_ACCOUNT_MISMATCH); } { @@ -885,7 +885,7 @@ applyClaimAttestations( if (attDstChain != dstChain) { - return Unexpected(tecXCHAIN_WRONG_CHAIN); + return std::unexpected(tecXCHAIN_WRONG_CHAIN); } } @@ -964,10 +964,10 @@ applyCreateAccountAttestations( PaymentSandbox psb(&view); - auto const claimCountResult = [&]() -> Expected { + auto const claimCountResult = [&]() -> std::expected { auto const sleBridge = psb.peek(bridgeK); if (!sleBridge) - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); return (*sleBridge)[sfXChainAccountClaimCount]; }(); @@ -1009,7 +1009,7 @@ applyCreateAccountAttestations( XChainCreateAccountAttestations curAtts; }; - auto const scopeResult = [&]() -> Expected { + auto const scopeResult = [&]() -> std::expected { // This lambda is ugly - admittedly. The purpose of this lambda is to // limit the scope of sles so they don't overlap with // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child @@ -1025,14 +1025,14 @@ applyCreateAccountAttestations( auto const sleDoor = psb.peek(doorK); if (!sleDoor) - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); // Check reserve auto const balance = (*sleDoor)[sfBalance]; auto const reserve = psb.fees().accountReserve((*sleDoor)[sfOwnerCount] + 1); if (balance < reserve) - return Unexpected(tecINSUFFICIENT_RESERVE); + return std::unexpected(tecINSUFFICIENT_RESERVE); } std::vector atts; @@ -1045,7 +1045,7 @@ applyCreateAccountAttestations( } if (atts.empty()) { - return Unexpected(tecXCHAIN_PROOF_UNKNOWN_KEY); + return std::unexpected(tecXCHAIN_PROOF_UNKNOWN_KEY); } XChainCreateAccountAttestations curAtts = [&] { @@ -1071,7 +1071,7 @@ applyCreateAccountAttestations( // Modify the object before it's potentially deleted, so the meta // data will include the new attestations if (!sleClaimID) - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); sleClaimID->setFieldArray(sfXChainCreateAccountAttestations, curAtts.toSTArray()); psb.update(sleClaimID); } @@ -1244,7 +1244,7 @@ attestationDoApply(ApplyContext& ctx) Keylet bridgeK; }; - auto const scopeResult = [&]() -> Expected { + auto const scopeResult = [&]() -> std::expected { // This lambda is ugly - admittedly. The purpose of this lambda is to // limit the scope of sles so they don't overlap with // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child @@ -1252,7 +1252,7 @@ attestationDoApply(ApplyContext& ctx) auto sleBridge = readBridge(ctx.view(), bridgeSpec); if (!sleBridge) { - return Unexpected(tecNO_ENTRY); + return std::unexpected(tecNO_ENTRY); } Keylet const bridgeK{ltBRIDGE, sleBridge->key()}; AccountID const thisDoor = (*sleBridge)[sfAccount]; @@ -1269,7 +1269,7 @@ attestationDoApply(ApplyContext& ctx) } else { - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); } } STXChainBridge::ChainType const srcChain = STXChainBridge::otherChain(dstChain); @@ -1279,7 +1279,7 @@ attestationDoApply(ApplyContext& ctx) getSignersListAndQuorum(ctx.view(), *sleBridge, ctx.journal); if (!isTesSuccess(slTer)) - return Unexpected(slTer); + return std::unexpected(slTer); return ScopeResult{srcChain, std::move(signersList), quorum, thisDoor, bridgeK}; }(); @@ -1721,7 +1721,7 @@ XChainClaim::doApply() STAmount signatureReward; }; - auto const scopeResult = [&]() -> Expected { + auto const scopeResult = [&]() -> std::expected { // This lambda is ugly - admittedly. The purpose of this lambda is to // limit the scope of sles so they don't overlap with // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child @@ -1732,7 +1732,7 @@ XChainClaim::doApply() auto const sleClaimID = psb.peek(claimIDKeylet); if (!(sleBridge && sleClaimID && sleAcct)) - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); AccountID const thisDoor = (*sleBridge)[sfAccount]; @@ -1748,7 +1748,7 @@ XChainClaim::doApply() } else { - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); } } STXChainBridge::ChainType const srcChain = STXChainBridge::otherChain(dstChain); @@ -1763,7 +1763,7 @@ XChainClaim::doApply() getSignersListAndQuorum(ctx_.view(), *sleBridge, ctx_.journal); if (!isTesSuccess(slTer)) - return Unexpected(slTer); + return std::unexpected(slTer); XChainClaimAttestations curAtts{sleClaimID->getFieldArray(sfXChainClaimAttestations)}; @@ -1776,7 +1776,7 @@ XChainClaim::doApply() signersList, ctx_.journal); if (!claimR.has_value()) - return Unexpected(claimR.error()); + return std::unexpected(claimR.error()); return ScopeResult{ .rewardAccounts = claimR.value(), diff --git a/src/libxrpl/tx/transactors/dex/AMMBid.cpp b/src/libxrpl/tx/transactors/dex/AMMBid.cpp index a98f439d0a..83432fa0d0 100644 --- a/src/libxrpl/tx/transactors/dex/AMMBid.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMBid.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -27,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -266,7 +266,7 @@ applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journa auto const bidMin = ctx.tx[~sfBidMin]; auto const bidMax = ctx.tx[~sfBidMax]; - auto getPayPrice = [&](Number const& computedPrice) -> Expected { + auto getPayPrice = [&](Number const& computedPrice) -> std::expected { auto const payPrice = [&]() -> std::optional { // Both min/max bid price are defined if (bidMin && bidMax) @@ -295,11 +295,11 @@ applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journa }(); if (!payPrice) { - return Unexpected(tecAMM_FAILED); + return std::unexpected(tecAMM_FAILED); } if (payPrice > lpTokens) { - return Unexpected(tecAMM_INVALID_TOKENS); + return std::unexpected(tecAMM_INVALID_TOKENS); } return *payPrice; }; diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp index 0e1a4b3a3d..041bf73abf 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerCoverClawback.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include +#include #include #include @@ -83,7 +83,7 @@ LoanBrokerCoverClawback::preflight(PreflightContext const& ctx) return tesSUCCESS; } -Expected +std::expected determineBrokerID(ReadView const& view, STTx const& tx) { // If the broker ID was provided in the transaction, that's all we @@ -96,7 +96,7 @@ determineBrokerID(ReadView const& view, STTx const& tx) // because that should have been rejected in preflight(). auto const dstAmount = tx[~sfAmount]; if (!dstAmount || !dstAmount->holds()) - return Unexpected{tecINTERNAL}; // LCOV_EXCL_LINE + return std::unexpected{tecINTERNAL}; // LCOV_EXCL_LINE // Every trust line is bidirectional. Both sides are simultaneously // issuer and holder. For this transaction, the Account is acting as @@ -112,7 +112,7 @@ determineBrokerID(ReadView const& view, STTx const& tx) // If the account was not found, the transaction can't go further. if (!sle) - return Unexpected{tecNO_ENTRY}; + return std::unexpected{tecNO_ENTRY}; // If the account was found, and has a LoanBrokerID (and therefore // is a pseudo-account), that's the @@ -122,11 +122,11 @@ determineBrokerID(ReadView const& view, STTx const& tx) // If the account does not have a LoanBrokerID, the transaction // can't go further, even if it's a different type of Pseudo-account. - return Unexpected{tecOBJECT_NOT_FOUND}; + return std::unexpected{tecOBJECT_NOT_FOUND}; // Or tecWRONG_ASSET? } -Expected +std::expected determineAsset( ReadView const& view, AccountID const& account, @@ -153,10 +153,10 @@ determineAsset( return Issue{amount.get().currency, account}; } - return Unexpected(tecWRONG_ASSET); + return std::unexpected(tecWRONG_ASSET); } -Expected +std::expected determineClawAmount( SLE const& sleBroker, Asset const& vaultAsset, @@ -182,7 +182,7 @@ determineClawAmount( return sleBroker[sfCoverAvailable] - minRequiredCover; }(); if (maxClawAmount <= beast::kZero) - return Unexpected(tecINSUFFICIENT_FUNDS); + return std::unexpected(tecINSUFFICIENT_FUNDS); // Use the vaultAsset here, because it will be the right type in all // circumstances. The amount may be an IOU indicating the pseudo-account's diff --git a/src/libxrpl/tx/transactors/lending/LoanPay.cpp b/src/libxrpl/tx/transactors/lending/LoanPay.cpp index a0a1479bdb..5c0b46de42 100644 --- a/src/libxrpl/tx/transactors/lending/LoanPay.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanPay.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -29,6 +28,7 @@ #include #include #include +#include #include namespace xrpl { @@ -380,7 +380,7 @@ LoanPay::doApply() return LoanPaymentType::Regular; }(); - Expected const paymentParts = + std::expected const paymentParts = loanMakePayment(asset, view, loanSle, brokerSle, amount, paymentType, j_); if (!paymentParts) diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index bd7413d50b..d8e5a7b235 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -26,6 +25,7 @@ #include #include #include +#include #include // IWYU pragma: keep #include @@ -223,12 +223,12 @@ NFTokenMint::doApply() { auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_); - auto const tokenSeq = [this, &issuer]() -> Expected { + auto const tokenSeq = [this, &issuer]() -> std::expected { auto const root = view().peek(keylet::account(issuer)); if (root == nullptr) { // Should not happen. Checked in preclaim. - return Unexpected(tecNO_ISSUER); + return std::unexpected(tecNO_ISSUER); } // If the issuer hasn't minted an NFToken before we must add a @@ -259,7 +259,7 @@ NFTokenMint::doApply() (*root)[sfMintedNFTokens] = mintedNftCnt + 1u; if ((*root)[sfMintedNFTokens] == 0u) - return Unexpected(tecMAX_SEQUENCE_REACHED); + return std::unexpected(tecMAX_SEQUENCE_REACHED); // Get the unique sequence number of this token by // sfFirstNFTokenSequence + sfMintedNFTokens @@ -268,7 +268,7 @@ NFTokenMint::doApply() // Check for more overflow cases if (tokenSeq + 1u == 0u || tokenSeq < offset) - return Unexpected(tecMAX_SEQUENCE_REACHED); + return std::unexpected(tecMAX_SEQUENCE_REACHED); ctx_.view().update(root); return tokenSeq; diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index 94a5ca848d..90e33d3a70 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -22,6 +21,7 @@ #include #include +#include #include namespace xrpl { @@ -100,16 +100,16 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) return tesSUCCESS; } -Expected +std::expected MPTokenIssuanceCreate::create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args) { auto const acct = view.peek(keylet::account(args.account)); if (!acct) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE if (args.priorBalance && *(args.priorBalance) < view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) - return Unexpected(tecINSUFFICIENT_RESERVE); + return std::unexpected(tecINSUFFICIENT_RESERVE); auto const mptId = makeMptID(args.sequence, args.account); auto const mptIssuanceKeylet = keylet::mptIssuance(mptId); @@ -120,7 +120,7 @@ MPTokenIssuanceCreate::create(ApplyView& view, beast::Journal journal, MPTCreate keylet::ownerDir(args.account), mptIssuanceKeylet, describeOwnerDir(args.account)); if (!ownerNode) - return Unexpected(tecDIR_FULL); // LCOV_EXCL_LINE + return std::unexpected(tecDIR_FULL); // LCOV_EXCL_LINE auto mptIssuance = std::make_shared(mptIssuanceKeylet); (*mptIssuance)[sfFlags] = args.flags & ~tfUniversal; @@ -156,10 +156,10 @@ MPTokenIssuanceCreate::create(ApplyView& view, beast::Journal journal, MPTCreate // would dangle the pointer and is a programmer error. auto const sleHolding = view.read(keylet::unchecked(*args.referenceHolding)); if (!sleHolding) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE auto const type = sleHolding->getType(); if (type != ltMPTOKEN && type != ltRIPPLE_STATE) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE (*mptIssuance)[sfReferenceHolding] = *args.referenceHolding; } diff --git a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp index eb12905467..a8587feaeb 100644 --- a/src/libxrpl/tx/transactors/vault/VaultClawback.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultClawback.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -26,6 +25,7 @@ #include #include +#include #include #include #include @@ -218,7 +218,7 @@ VaultClawback::preclaim(PreclaimContext const& ctx) return tecWRONG_ASSET; } -Expected, TER> +std::expected, TER> VaultClawback::assetsToClawback( SLE::ref vault, SLE::const_ref sleShareIssuance, @@ -230,7 +230,7 @@ VaultClawback::assetsToClawback( // preclaim should have blocked this , now it's an internal error // LCOV_EXCL_START JLOG(j_.error()) << "VaultClawback: asset mismatch in clawback."; - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); // LCOV_EXCL_STOP } @@ -248,7 +248,7 @@ VaultClawback::assetsToClawback( view(), holder, share, FreezeHandling::IgnoreFreeze, AuthHandling::IgnoreAuth, j_); auto const maybeAssets = sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); if (!maybeAssets) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE return std::make_pair(*maybeAssets, sharesDestroyed); } @@ -265,7 +265,7 @@ VaultClawback::assetsToClawback( auto const maybeAssets = sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); if (!maybeAssets) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE assetsRecovered = *maybeAssets; } @@ -274,13 +274,13 @@ VaultClawback::assetsToClawback( auto const maybeShares = assetsToSharesWithdraw(vault, sleShareIssuance, clawbackAmount); if (!maybeShares) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE sharesDestroyed = *maybeShares; auto const maybeAssets = sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); if (!maybeAssets) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE assetsRecovered = *maybeAssets; } // Clamp to maximum. @@ -294,20 +294,20 @@ VaultClawback::assetsToClawback( auto const maybeShares = assetsToSharesWithdraw( vault, sleShareIssuance, assetsRecovered, TruncateShares::Yes); if (!maybeShares) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE sharesDestroyed = *maybeShares; } auto const maybeAssets = sharesToAssetsWithdraw(vault, sleShareIssuance, sharesDestroyed); if (!maybeAssets) - return Unexpected(tecINTERNAL); // LCOV_EXCL_LINE + return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE assetsRecovered = *maybeAssets; if (assetsRecovered > *assetsAvailable) { // LCOV_EXCL_START JLOG(j_.error()) << "VaultClawback: invalid rounding of shares."; - return Unexpected(tecINTERNAL); + return std::unexpected(tecINTERNAL); // LCOV_EXCL_STOP } } @@ -322,7 +322,7 @@ VaultClawback::assetsToClawback( << ", assetsTotal=" << vault->at(sfAssetsTotal).value() << ", sharesTotal=" << sleShareIssuance->at(sfOutstandingAmount) << ", amount=" << clawbackAmount.value(); - return Unexpected(tecPATH_DRY); + return std::unexpected(tecPATH_DRY); } return std::make_pair(assetsRecovered, sharesDestroyed); diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 588aeee634..70b091290c 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -2018,8 +2018,8 @@ class Delegate_test : public beast::unit_test::Suite auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result]; BEAST_EXPECT(jrr[jss::status] == "error"); BEAST_EXPECT( - jrr[jss::error_message].asString().find( - "A Signer may not be the transaction's Account") != std::string::npos); + jrr[jss::error_message].asString().contains( + "A Signer may not be the transaction's Account")); } } diff --git a/src/test/app/GRPCServerTLS_test.cpp b/src/test/app/GRPCServerTLS_test.cpp index ae0d839a6e..a48986d004 100644 --- a/src/test/app/GRPCServerTLS_test.cpp +++ b/src/test/app/GRPCServerTLS_test.cpp @@ -479,8 +479,7 @@ public: } catch (std::runtime_error const& e) { - BEAST_EXPECT( - std::string(e.what()).find("Incomplete TLS configuration") != std::string::npos); + BEAST_EXPECT(std::string(e.what()).contains("Incomplete TLS configuration")); } } @@ -505,8 +504,7 @@ public: } catch (std::runtime_error const& e) { - BEAST_EXPECT( - std::string(e.what()).find("Incomplete TLS configuration") != std::string::npos); + BEAST_EXPECT(std::string(e.what()).contains("Incomplete TLS configuration")); } } @@ -533,8 +531,8 @@ public: catch (std::runtime_error const& e) { BEAST_EXPECT( - std::string(e.what()).find( - "ssl_client_ca requires both ssl_cert and ssl_key") != std::string::npos); + std::string(e.what()).contains( + "ssl_client_ca requires both ssl_cert and ssl_key")); } } @@ -556,9 +554,7 @@ public: { // This should fail with "Incomplete TLS configuration" first // because ssl_cert is specified without ssl_key - BEAST_EXPECT( - std::string(e.what()).find("Incomplete TLS configuration") != - std::string::npos); + BEAST_EXPECT(std::string(e.what()).contains("Incomplete TLS configuration")); } } @@ -580,9 +576,7 @@ public: { // This should fail with "Incomplete TLS configuration" first // because ssl_key is specified without ssl_cert - BEAST_EXPECT( - std::string(e.what()).find("Incomplete TLS configuration") != - std::string::npos); + BEAST_EXPECT(std::string(e.what()).contains("Incomplete TLS configuration")); } } } @@ -610,8 +604,8 @@ public: catch (std::runtime_error const& e) { BEAST_EXPECT( - std::string(e.what()).find( - "ssl_cert_chain requires both ssl_cert and ssl_key") != std::string::npos); + std::string(e.what()).contains( + "ssl_cert_chain requires both ssl_cert and ssl_key")); } } @@ -633,9 +627,7 @@ public: { // This should fail with "Incomplete TLS configuration" first // because ssl_cert is specified without ssl_key - BEAST_EXPECT( - std::string(e.what()).find("Incomplete TLS configuration") != - std::string::npos); + BEAST_EXPECT(std::string(e.what()).contains("Incomplete TLS configuration")); } } } diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index 3654036869..6d53d25661 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -216,7 +216,7 @@ class Invariants_test : public beast::unit_test::Suite // std::cerr << messages << '\n'; for (auto const& m : expectLogs) { - BEAST_EXPECTS(messages.find(m) != std::string::npos, m); + BEAST_EXPECTS(messages.contains(m), m); } } } @@ -2187,8 +2187,7 @@ class Invariants_test : public beast::unit_test::Suite BEAST_EXPECT(!invariant.finalize( makeOfferCreateTx(), tesSUCCESS, XRPAmount{}, view, missingRootJlog)); BEAST_EXPECT( - missingRootSink.messages().str().find("book directory root missing") != - std::string::npos); + missingRootSink.messages().str().contains("book directory root missing")); } { // delete diff --git a/src/test/app/MultiSign_test.cpp b/src/test/app/MultiSign_test.cpp index 5092cafef9..f161226210 100644 --- a/src/test/app/MultiSign_test.cpp +++ b/src/test/app/MultiSign_test.cpp @@ -1121,8 +1121,8 @@ public: // Signature should fail. auto const info = submitSTTx(local); BEAST_EXPECT( - info[jss::result][jss::error_exception].asString().find( - "Invalid signature on account r") != std::string::npos); + info[jss::result][jss::error_exception].asString().contains( + "Invalid signature on account r")); } { // Multisign with an empty signers array should fail. diff --git a/src/test/app/NFTokenBurn_test.cpp b/src/test/app/NFTokenBurn_test.cpp index 482d275d51..46b02d03cf 100644 --- a/src/test/app/NFTokenBurn_test.cpp +++ b/src/test/app/NFTokenBurn_test.cpp @@ -794,9 +794,8 @@ class NFTokenBurn_test : public beast::unit_test::Suite BEAST_EXPECT(sink.messages().str().starts_with("Invariant failed:")); // uncomment to log the invariant failure message // log << " --> " << sink.messages().str() << std::endl; - BEAST_EXPECT( - sink.messages().str().find( - "Last NFT page deleted with non-empty directory") != std::string::npos); + BEAST_EXPECT(sink.messages().str().contains( + "Last NFT page deleted with non-empty directory")); } } { @@ -831,8 +830,7 @@ class NFTokenBurn_test : public beast::unit_test::Suite BEAST_EXPECT(sink.messages().str().starts_with("Invariant failed:")); // uncomment to log the invariant failure message // log << " --> " << sink.messages().str() << std::endl; - BEAST_EXPECT( - sink.messages().str().find("Lost NextMinPage link") != std::string::npos); + BEAST_EXPECT(sink.messages().str().contains("Lost NextMinPage link")); } } } diff --git a/src/test/app/NetworkOPs_test.cpp b/src/test/app/NetworkOPs_test.cpp index d0a692b894..c5c8d33706 100644 --- a/src/test/app/NetworkOPs_test.cpp +++ b/src/test/app/NetworkOPs_test.cpp @@ -54,7 +54,7 @@ public: env.close(); } - BEAST_EXPECT(logs.find("No transaction to process!") != std::string::npos); + BEAST_EXPECT(logs.contains("No transaction to process!")); } }; diff --git a/src/test/app/ValidatorSite_test.cpp b/src/test/app/ValidatorSite_test.cpp index e3e3ec27df..8400f2d794 100644 --- a/src/test/app/ValidatorSite_test.cpp +++ b/src/test/app/ValidatorSite_test.cpp @@ -239,7 +239,7 @@ private: json::Value myStatus; for (auto const& vs : jv[jss::validator_sites]) { - if (vs[jss::uri].asString().find(u.uri) != std::string::npos) + if (vs[jss::uri].asString().contains(u.uri)) myStatus = vs; } BEAST_EXPECTS( @@ -248,9 +248,7 @@ private: if (!u.cfg.msg.empty()) { - BEAST_EXPECTS( - sink.messages().str().find(u.cfg.msg) != std::string::npos, - sink.messages().str()); + BEAST_EXPECTS(sink.messages().str().contains(u.cfg.msg), sink.messages().str()); } if (u.cfg.expectedRefreshMin != 0) @@ -324,7 +322,7 @@ private: json::Value myStatus; for (auto const& vs : jv[jss::validator_sites]) { - if (vs[jss::uri].asString().find(u.uri) != std::string::npos) + if (vs[jss::uri].asString().contains(u.uri)) myStatus = vs; } BEAST_EXPECTS( @@ -332,9 +330,7 @@ private: to_string(myStatus)); if (u.shouldFail) { - BEAST_EXPECTS( - sink.messages().str().find(u.expectMsg) != std::string::npos, - sink.messages().str()); + BEAST_EXPECTS(sink.messages().str().contains(u.expectMsg), sink.messages().str()); } } } diff --git a/src/test/basics/Expected_test.cpp b/src/test/basics/Expected_test.cpp deleted file mode 100644 index c8e570835b..0000000000 --- a/src/test/basics/Expected_test.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#if BOOST_VERSION >= 107500 -#endif // BOOST_VERSION -#include - -namespace xrpl::test { - -struct Expected_test : beast::unit_test::Suite -{ - void - run() override - { - // Test non-error const construction. - { - auto const expected = []() -> Expected { return "Valid value"; }(); - BEAST_EXPECT(expected); - BEAST_EXPECT(expected.has_value()); - BEAST_EXPECT(expected.value() == "Valid value"); - BEAST_EXPECT(*expected == "Valid value"); - BEAST_EXPECT(expected->at(0) == 'V'); - - bool throwOccurred = false; - try - { - // There's no error, so should throw. - [[maybe_unused]] TER const t = expected.error(); - } - catch (std::runtime_error const& e) - { - BEAST_EXPECT(e.what() == std::string("bad expected access")); - throwOccurred = true; - } - BEAST_EXPECT(throwOccurred); - } - // Test non-error non-const construction. - { - auto expected = []() -> Expected { return "Valid value"; }(); - BEAST_EXPECT(expected); - BEAST_EXPECT(expected.has_value()); - BEAST_EXPECT(expected.value() == "Valid value"); - BEAST_EXPECT(*expected == "Valid value"); - BEAST_EXPECT(expected->at(0) == 'V'); - std::string const mv = std::move(*expected); - BEAST_EXPECT(mv == "Valid value"); - - bool throwOccurred = false; - try - { - // There's no error, so should throw. - [[maybe_unused]] TER const t = expected.error(); - } - catch (std::runtime_error const& e) - { - BEAST_EXPECT(e.what() == std::string("bad expected access")); - throwOccurred = true; - } - BEAST_EXPECT(throwOccurred); - } - // Test non-error overlapping type construction. - { - auto expected = []() -> Expected { return 1; }(); - BEAST_EXPECT(expected); - BEAST_EXPECT(expected.has_value()); - BEAST_EXPECT(expected.value() == 1); - BEAST_EXPECT(*expected == 1); - - bool throwOccurred = false; - try - { - // There's no error, so should throw. - [[maybe_unused]] std::uint16_t const t = expected.error(); - } - catch (std::runtime_error const& e) - { - BEAST_EXPECT(e.what() == std::string("bad expected access")); - throwOccurred = true; - } - BEAST_EXPECT(throwOccurred); - } - // Test error construction from rvalue. - { - auto const expected = []() -> Expected { - return Unexpected(telLOCAL_ERROR); - }(); - BEAST_EXPECT(!expected); - BEAST_EXPECT(!expected.has_value()); - BEAST_EXPECT(expected.error() == telLOCAL_ERROR); - - bool throwOccurred = false; - try - { - // There's no result, so should throw. - [[maybe_unused]] std::string const s = *expected; - } - catch (std::runtime_error const& e) - { - BEAST_EXPECT(e.what() == std::string("bad expected access")); - throwOccurred = true; - } - BEAST_EXPECT(throwOccurred); - } - // Test error construction from lvalue. - { - auto const err(telLOCAL_ERROR); - auto expected = [&err]() -> Expected { return Unexpected(err); }(); - BEAST_EXPECT(!expected); - BEAST_EXPECT(!expected.has_value()); - BEAST_EXPECT(expected.error() == telLOCAL_ERROR); - - bool throwOccurred = false; - try - { - // There's no result, so should throw. - [[maybe_unused]] std::size_t const s = expected->size(); - } - catch (std::runtime_error const& e) - { - BEAST_EXPECT(e.what() == std::string("bad expected access")); - throwOccurred = true; - } - BEAST_EXPECT(throwOccurred); - } - // Test error construction from const char*. - { - auto const expected = []() -> Expected { - return Unexpected("Not what is expected!"); - }(); - BEAST_EXPECT(!expected); - BEAST_EXPECT(!expected.has_value()); - BEAST_EXPECT(expected.error() == std::string("Not what is expected!")); - } - // Test error construction of string from const char*. - { - auto expected = []() -> Expected { - return Unexpected("Not what is expected!"); - }(); - BEAST_EXPECT(!expected); - BEAST_EXPECT(!expected.has_value()); - BEAST_EXPECT(expected.error() == "Not what is expected!"); - std::string const s(std::move(expected.error())); - BEAST_EXPECT(s == "Not what is expected!"); - } - // Test non-error const construction of Expected. - { - auto const expected = []() -> Expected { return {}; }(); - BEAST_EXPECT(expected); - bool throwOccurred = false; - try - { - // There's no error, so should throw. - [[maybe_unused]] std::size_t const s = expected.error().size(); - } - catch (std::runtime_error const& e) - { - BEAST_EXPECT(e.what() == std::string("bad expected access")); - throwOccurred = true; - } - BEAST_EXPECT(throwOccurred); - } - // Test non-error non-const construction of Expected. - { - auto expected = []() -> Expected { return {}; }(); - BEAST_EXPECT(expected); - bool throwOccurred = false; - try - { - // There's no error, so should throw. - [[maybe_unused]] std::size_t const s = expected.error().size(); - } - catch (std::runtime_error const& e) - { - BEAST_EXPECT(e.what() == std::string("bad expected access")); - throwOccurred = true; - } - BEAST_EXPECT(throwOccurred); - } - // Test error const construction of Expected. - { - auto const expected = []() -> Expected { - return Unexpected("Not what is expected!"); - }(); - BEAST_EXPECT(!expected); - BEAST_EXPECT(expected.error() == "Not what is expected!"); - } - // Test error non-const construction of Expected. - { - auto expected = []() -> Expected { - return Unexpected("Not what is expected!"); - }(); - BEAST_EXPECT(!expected); - BEAST_EXPECT(expected.error() == "Not what is expected!"); - std::string const s(std::move(expected.error())); - BEAST_EXPECT(s == "Not what is expected!"); - } - // Test a case that previously unintentionally returned an array. -#if BOOST_VERSION >= 107500 - { - auto expected = []() -> Expected { - return boost::json::object{{"oops", "me array now"}}; - }(); - BEAST_EXPECT(expected); - BEAST_EXPECT(!expected.value().is_array()); - } -#endif // BOOST_VERSION - } -}; - -BEAST_DEFINE_TESTSUITE(Expected, basics, xrpl); - -} // namespace xrpl::test diff --git a/src/test/consensus/Consensus_test.cpp b/src/test/consensus/Consensus_test.cpp index 6cc8d4fde1..629a97f0ea 100644 --- a/src/test/consensus/Consensus_test.cpp +++ b/src/test/consensus/Consensus_test.cpp @@ -1296,14 +1296,11 @@ public: auto const s = clog->str(); expect(s.find("stalled"), s, __FILE__, line); expect(s.starts_with("Transaction "s + std::to_string(txid)), s, __FILE__, line); - expect(s.find("voting "s + (ourVote ? "YES" : "NO")) != s.npos, s, __FILE__, line); + expect(s.contains("voting "s + (ourVote ? "YES" : "NO")), s, __FILE__, line); expect( - s.find("for "s + std::to_string(ourTime) + " rounds."s) != s.npos, - s, - __FILE__, - line); + s.contains("for "s + std::to_string(ourTime) + " rounds."s), s, __FILE__, line); expect( - s.find("votes in "s + std::to_string(peerTime) + " rounds.") != s.npos, + s.contains("votes in "s + std::to_string(peerTime) + " rounds."), s, __FILE__, line); diff --git a/src/test/jtx/CheckMessageLogs.h b/src/test/jtx/CheckMessageLogs.h index 2cc8c661eb..4bbdec6f06 100644 --- a/src/test/jtx/CheckMessageLogs.h +++ b/src/test/jtx/CheckMessageLogs.h @@ -24,7 +24,7 @@ class CheckMessageLogs : public Logs void write(beast::Severity level, std::string const& text) override { - if (text.find(owner_.msg_) != std::string::npos) + if (text.contains(owner_.msg_)) *owner_.pFound_ = true; } diff --git a/src/test/jtx/directory.h b/src/test/jtx/directory.h index 9a87266802..c63640ae1f 100644 --- a/src/test/jtx/directory.h +++ b/src/test/jtx/directory.h @@ -2,11 +2,11 @@ #include -#include #include #include #include +#include #include /** Directory operations. */ @@ -34,7 +34,7 @@ bumpLastPage( Env& env, std::uint64_t newLastPage, Keylet directory, - std::function adjust) -> Expected; + 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 diff --git a/src/test/jtx/impl/directory.cpp b/src/test/jtx/impl/directory.cpp index 7a92c2bf25..9fb06e25df 100644 --- a/src/test/jtx/impl/directory.cpp +++ b/src/test/jtx/impl/directory.cpp @@ -2,7 +2,6 @@ #include -#include #include #include #include @@ -15,6 +14,7 @@ #include #include +#include #include #include @@ -26,9 +26,9 @@ bumpLastPage( Env& env, std::uint64_t newLastPage, Keylet directory, - std::function adjust) -> Expected + std::function adjust) -> std::expected { - Expected res{}; + std::expected res{}; env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) -> bool { Sandbox sb(&view, TapNone); @@ -36,7 +36,7 @@ bumpLastPage( auto sleRoot = sb.peek(directory); if (!sleRoot) { - res = Unexpected(Error::DirectoryRootNotFound); + res = std::unexpected(Error::DirectoryRootNotFound); return false; } @@ -44,26 +44,26 @@ bumpLastPage( auto const lastIndex = sleRoot->getFieldU64(sfIndexPrevious); if (lastIndex == 0) { - res = Unexpected(Error::DirectoryTooSmall); + res = std::unexpected(Error::DirectoryTooSmall); return false; } if (sb.exists(keylet::page(directory, newLastPage))) { - res = Unexpected(Error::DirectoryPageDuplicate); + res = std::unexpected(Error::DirectoryPageDuplicate); return false; } if (lastIndex >= newLastPage) { - res = Unexpected(Error::InvalidLastPage); + res = std::unexpected(Error::InvalidLastPage); return false; } auto slePage = sb.peek(keylet::page(directory, lastIndex)); if (!slePage) { - res = Unexpected(Error::DirectoryPageNotFound); + res = std::unexpected(Error::DirectoryPageNotFound); return false; } @@ -94,7 +94,7 @@ bumpLastPage( auto slePrev = sb.peek(keylet::page(directory, *prevIndex)); if (!slePrev) { - res = Unexpected(Error::DirectoryPageNotFound); + res = std::unexpected(Error::DirectoryPageNotFound); return false; } slePrev->setFieldU64(sfIndexNext, newLastPage); @@ -109,7 +109,7 @@ bumpLastPage( { if (!adjust(sb, key, newLastPage)) { - res = Unexpected(Error::AdjustmentError); + res = std::unexpected(Error::AdjustmentError); return false; } } diff --git a/src/test/nodestore/NuDBFactory_test.cpp b/src/test/nodestore/NuDBFactory_test.cpp index 3ca3fa6838..d0675b3893 100644 --- a/src/test/nodestore/NuDBFactory_test.cpp +++ b/src/test/nodestore/NuDBFactory_test.cpp @@ -88,7 +88,7 @@ private: auto backend = Manager::instance().makeBackend(params, megabytes(4), scheduler, journal); std::string const logOutput = sink.messages().str(); - BEAST_EXPECT(logOutput.find(expectedMessage) != std::string::npos); + BEAST_EXPECT(logOutput.contains(expectedMessage)); } // Helper function to test power of two validation @@ -105,7 +105,7 @@ private: auto backend = Manager::instance().makeBackend(params, megabytes(4), scheduler, journal); std::string const logOutput = sink.messages().str(); - bool const hasWarning = logOutput.find("Invalid nudb_block_size") != std::string::npos; + bool const hasWarning = logOutput.contains("Invalid nudb_block_size"); BEAST_EXPECT(hasWarning == !shouldWork); } @@ -221,10 +221,8 @@ public: catch (std::exception const& e) { std::string const logOutput{e.what()}; - BEAST_EXPECT(logOutput.find("Invalid nudb_block_size: 5000") != std::string::npos); - BEAST_EXPECT( - logOutput.find("Must be power of 2 between 4096 and 32768") != - std::string::npos); + BEAST_EXPECT(logOutput.contains("Invalid nudb_block_size: 5000")); + BEAST_EXPECT(logOutput.contains("Must be power of 2 between 4096 and 32768")); } } @@ -247,8 +245,7 @@ public: catch (std::exception const& e) { std::string const logOutput{e.what()}; - BEAST_EXPECT( - logOutput.find("Invalid nudb_block_size value: invalid") != std::string::npos); + BEAST_EXPECT(logOutput.contains("Invalid nudb_block_size value: invalid")); } } } @@ -291,7 +288,7 @@ public: catch (std::exception const& e) { std::string const logOutput{e.what()}; - BEAST_EXPECT(logOutput.find("Invalid nudb_block_size") != std::string::npos); + BEAST_EXPECT(logOutput.contains("Invalid nudb_block_size")); } } } @@ -355,8 +352,7 @@ public: // Should log success message for valid values std::string const logOutput = sink.messages().str(); - bool const hasSuccessMessage = - logOutput.find("Using custom NuDB block size") != std::string::npos; + bool const hasSuccessMessage = logOutput.contains("Using custom NuDB block size"); BEAST_EXPECT(hasSuccessMessage); } diff --git a/src/test/overlay/reduce_relay_test.cpp b/src/test/overlay/reduce_relay_test.cpp index 54d8f555a2..1c9bf1f9c1 100644 --- a/src/test/overlay/reduce_relay_test.cpp +++ b/src/test/overlay/reduce_relay_test.cpp @@ -806,7 +806,7 @@ public: { auto size = max - min; std::vector s(size); - std::iota(s.begin(), s.end(), min); + std::iota(s.begin(), s.end(), min); // NOLINT(modernize-use-ranges) std::random_device d; std::mt19937 g(d()); std::shuffle(s.begin(), s.end(), g); diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index 5ba71962b3..4a9c12c96b 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -801,7 +801,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En if (!BEAST_EXPECTS(!ec, ec.message())) return; BEAST_EXPECT(resp.result() == boost::beast::http::status::ok); - BEAST_EXPECT(resp.body().find("connectivity is working.") != std::string::npos); + BEAST_EXPECT(resp.body().contains("connectivity is working.")); // mark the Network as having an Amendment Warning, but won't fail env.app().getOPs().setAmendmentWarned(); @@ -846,7 +846,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En if (!BEAST_EXPECTS(!ec, ec.message())) return; BEAST_EXPECT(resp.result() == boost::beast::http::status::ok); - BEAST_EXPECT(resp.body().find("connectivity is working.") != std::string::npos); + BEAST_EXPECT(resp.body().contains("connectivity is working.")); // with ELB_SUPPORT, status still does not indicate a problem env.app().config().elbSupport = true; @@ -866,7 +866,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En if (!BEAST_EXPECTS(!ec, ec.message())) return; BEAST_EXPECT(resp.result() == boost::beast::http::status::ok); - BEAST_EXPECT(resp.body().find("connectivity is working.") != std::string::npos); + BEAST_EXPECT(resp.body().contains("connectivity is working.")); } void @@ -929,7 +929,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En if (!BEAST_EXPECTS(!ec, ec.message())) return; BEAST_EXPECT(resp.result() == boost::beast::http::status::ok); - BEAST_EXPECT(resp.body().find("connectivity is working.") != std::string::npos); + BEAST_EXPECT(resp.body().contains("connectivity is working.")); // mark the Network as Amendment Blocked, but still won't fail until // ELB is enabled (next step) @@ -977,7 +977,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En if (!BEAST_EXPECTS(!ec, ec.message())) return; BEAST_EXPECT(resp.result() == boost::beast::http::status::ok); - BEAST_EXPECT(resp.body().find("connectivity is working.") != std::string::npos); + BEAST_EXPECT(resp.body().contains("connectivity is working.")); env.app().config().elbSupport = true; @@ -996,8 +996,8 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En if (!BEAST_EXPECTS(!ec, ec.message())) return; BEAST_EXPECT(resp.result() == boost::beast::http::status::internal_server_error); - BEAST_EXPECT(resp.body().find("cannot accept clients:") != std::string::npos); - BEAST_EXPECT(resp.body().find("Server version too old") != std::string::npos); + BEAST_EXPECT(resp.body().contains("cannot accept clients:")); + BEAST_EXPECT(resp.body().contains("Server version too old")); } void diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp index 455a365b7c..54091ef767 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -401,7 +401,7 @@ public: }), std::make_unique(&messages)}; }); - BEAST_EXPECT(messages.find("Missing 'ip' in [port_rpc]") != std::string::npos); + BEAST_EXPECT(messages.contains("Missing 'ip' in [port_rpc]")); except([&] { Env const env{ @@ -413,7 +413,7 @@ public: }), std::make_unique(&messages)}; }); - BEAST_EXPECT(messages.find("Missing 'port' in [port_rpc]") != std::string::npos); + BEAST_EXPECT(messages.contains("Missing 'port' in [port_rpc]")); except([&] { Env const env{ @@ -426,8 +426,7 @@ public: }), std::make_unique(&messages)}; }); - BEAST_EXPECT( - messages.find("Invalid value '0' for key 'port' in [port_rpc]") == std::string::npos); + BEAST_EXPECT(!messages.contains("Invalid value '0' for key 'port' in [port_rpc]")); except([&] { Env const env{ @@ -438,8 +437,7 @@ public: }), std::make_unique(&messages)}; }); - BEAST_EXPECT( - messages.find("Invalid value '0' for key 'port' in [server]") != std::string::npos); + BEAST_EXPECT(messages.contains("Invalid value '0' for key 'port' in [server]")); except([&] { Env const env{ @@ -453,7 +451,7 @@ public: }), std::make_unique(&messages)}; }); - BEAST_EXPECT(messages.find("Missing 'protocol' in [port_rpc]") != std::string::npos); + BEAST_EXPECT(messages.contains("Missing 'protocol' in [port_rpc]")); except([&] // this creates a standard test config without the server // section @@ -482,7 +480,7 @@ public: }), std::make_unique(&messages)}; }); - BEAST_EXPECT(messages.find("Required section [server] is missing") != std::string::npos); + BEAST_EXPECT(messages.contains("Required section [server] is missing")); except([&] // this creates a standard test config without some of the // port sections @@ -503,7 +501,7 @@ public: }), std::make_unique(&messages)}; }); - BEAST_EXPECT(messages.find("Missing section: [port_peer]") != std::string::npos); + BEAST_EXPECT(messages.contains("Missing section: [port_peer]")); } void diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 325f8ba038..323dc14673 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -783,7 +783,7 @@ PeerImp::onShutdown(error_code ec) // - broken_pipe: the peer is gone bool const shouldLog = (ec != boost::asio::error::eof && ec != boost::asio::error::operation_aborted && - ec.message().find("application data after close notify") == std::string::npos); + !ec.message().contains("application data after close notify")); if (shouldLog) { diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp index 4d0d10a66b..38827dc93c 100644 --- a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -24,6 +23,7 @@ #include #include +#include #include namespace xrpl::RPC { @@ -381,7 +381,7 @@ lookupLedger(std::shared_ptr& ledger, JsonContext const& context return result; } -Expected, json::Value> +std::expected, json::Value> getOrAcquireLedger(RPC::JsonContext const& context) { auto const hasHash = context.params.isMember(jss::ledger_hash); @@ -393,7 +393,7 @@ getOrAcquireLedger(RPC::JsonContext const& context) if ((static_cast(hasHash) + static_cast(hasIndex)) != 1) { - return Unexpected( + return std::unexpected( RPC::makeParamError( "Exactly one of 'ledger_hash' or " "'ledger_index' can be specified.")); @@ -403,29 +403,29 @@ getOrAcquireLedger(RPC::JsonContext const& context) { auto const& jsonHash = context.params.get(jss::ledger_hash, json::ValueType::Null); if (!jsonHash.isString() || !ledgerHash.parseHex(jsonHash.asString())) - return Unexpected(RPC::expectedFieldError(jss::ledger_hash, "hex string")); + return std::unexpected(RPC::expectedFieldError(jss::ledger_hash, "hex string")); } else { auto const& jsonIndex = context.params.get(jss::ledger_index, json::ValueType::Null); if (!jsonIndex.isInt() && !jsonIndex.isUInt()) - return Unexpected(RPC::expectedFieldError(jss::ledger_index, "number")); + return std::unexpected(RPC::expectedFieldError(jss::ledger_index, "number")); // We need a validated ledger to get the hash from the sequence if (ledgerMaster.getValidatedLedgerAge() > RPC::Tuning::kMaxValidatedLedgerAge) { if (context.apiVersion == 1) - return Unexpected(rpcError(RpcNoCurrent)); - return Unexpected(rpcError(RpcNotSynced)); + return std::unexpected(rpcError(RpcNoCurrent)); + return std::unexpected(rpcError(RpcNotSynced)); } ledgerIndex = jsonIndex.asInt(); auto ledger = ledgerMaster.getValidatedLedger(); if (ledgerIndex >= ledger->header().seq) - return Unexpected(RPC::makeParamError("Ledger index too large")); + return std::unexpected(RPC::makeParamError("Ledger index too large")); if (ledgerIndex <= 0) - return Unexpected(RPC::makeParamError("Ledger index too small")); + return std::unexpected(RPC::makeParamError("Ledger index too small")); auto const j = context.app.getJournal("RPCHandler"); // Try to get the hash of the desired ledger from the validated @@ -452,7 +452,7 @@ getOrAcquireLedger(RPC::JsonContext const& context) json::Value jvResult = RPC::makeError( RpcLgrNotFound, "acquiring ledger containing requested index"); jvResult[jss::acquiring] = getJson(LedgerFill(*il, &context)); - return Unexpected(jvResult); + return std::unexpected(jvResult); } if (auto il = context.app.getInboundLedgers().find(*refHash)) @@ -461,11 +461,11 @@ getOrAcquireLedger(RPC::JsonContext const& context) json::Value jvResult = RPC::makeError( RpcLgrNotFound, "acquiring ledger containing requested index"); jvResult[jss::acquiring] = il->getJson(0); - return Unexpected(jvResult); + return std::unexpected(jvResult); } // Likely the app is shutting down - return Unexpected(json::Value()); + return std::unexpected(json::Value()); } neededHash = hashOfSeq(*ledger, ledgerIndex, j); @@ -487,9 +487,10 @@ getOrAcquireLedger(RPC::JsonContext const& context) return ledger; if (auto il = context.app.getInboundLedgers().find(ledgerHash)) - return Unexpected(il->getJson(0)); + return std::unexpected(il->getJson(0)); - return Unexpected(RPC::makeError(RpcNotReady, "findCreate failed to return an inbound ledger")); + return std::unexpected( + RPC::makeError(RpcNotReady, "findCreate failed to return an inbound ledger")); } } // namespace xrpl::RPC diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.h b/src/xrpld/rpc/detail/RPCLedgerHelpers.h index faec4ae069..9ec2a6673c 100644 --- a/src/xrpld/rpc/detail/RPCLedgerHelpers.h +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace xrpl { @@ -163,11 +164,11 @@ ledgerFromSpecifier( * * @param context The RPC JsonContext containing request parameters and * environment. - * @return Expected, json::Value> + * @return std::expected, json::Value> * On success, contains a shared pointer to the requested Ledger. * On failure, contains a json::Value describing the error. */ -Expected, json::Value> +std::expected, json::Value> getOrAcquireLedger(RPC::JsonContext const& context); } // namespace RPC diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 86d895fa1b..8c3a5ea245 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -57,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -407,23 +407,23 @@ checkTxJsonFields( return ret; } -static Expected +static std::expected checkNetworkID(json::Value const& txJson, uint32_t appNetworkId) { if (appNetworkId > 1024) { if (!txJson.isMember(jss::NetworkID)) { - return Unexpected( + return std::unexpected( RPC::makeError(RpcInvalidParams, RPC::missingFieldMessage("tx_json.NetworkID"))); } if (!txJson[jss::NetworkID].isIntegral() || txJson[jss::NetworkID].asUInt() != appNetworkId) { - return Unexpected( + return std::unexpected( RPC::makeError(RpcInvalidParams, RPC::invalidFieldMessage("tx_json.NetworkID"))); } } - return Expected(); + return std::expected(); } //------------------------------------------------------------------------------ diff --git a/src/xrpld/rpc/handlers/ledger/Ledger.cpp b/src/xrpld/rpc/handlers/ledger/Ledger.cpp index 1b096002bd..5938c8c9c5 100644 --- a/src/xrpld/rpc/handlers/ledger/Ledger.cpp +++ b/src/xrpld/rpc/handlers/ledger/Ledger.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include +#include #include #include #include @@ -44,14 +44,14 @@ LedgerHandler::check() { auto const& params = context_.params; - auto getBool = [&](json::StaticString const& field) -> Expected { + auto getBool = [&](json::StaticString const& field) -> std::expected { if (!params.isMember(field)) { return false; } if (!params[field].isBool()) { - return Unexpected(RpcInvalidParams); + return std::unexpected(RpcInvalidParams); } return params[field].asBool(); diff --git a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp index 9a9119d2ba..236712f0c2 100644 --- a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -27,6 +26,7 @@ #include #include +#include #include #include #include @@ -34,12 +34,12 @@ namespace xrpl { -using FunctionType = std::function( +using FunctionType = std::function( json::Value const&, json::StaticString const, unsigned const apiVersion)>; -static Expected +static std::expected parseFixed( Keylet const& keylet, json::Value const& params, @@ -55,12 +55,12 @@ fixed(Keylet const& keylet) return [keylet]( json::Value const& params, json::StaticString const fieldName, - unsigned const apiVersion) -> Expected { + unsigned const apiVersion) -> std::expected { return parseFixed(keylet, params, fieldName, apiVersion); }; } -static Expected +static std::expected parseObjectID( json::Value const& params, json::StaticString const fieldName, @@ -73,7 +73,7 @@ parseObjectID( return LedgerEntryHelpers::invalidFieldError("malformedRequest", fieldName, expectedType); } -static Expected +static std::expected parseIndex(json::Value const& params, json::StaticString const fieldName, unsigned const apiVersion) { if (apiVersion > 2u && params.isString()) @@ -95,7 +95,7 @@ parseIndex(json::Value const& params, json::StaticString const fieldName, unsign return parseObjectID(params, fieldName, "hex string"); } -static Expected +static std::expected parseAccountRoot( json::Value const& params, json::StaticString const fieldName, @@ -111,7 +111,7 @@ parseAccountRoot( auto const parseAmendments = fixed(keylet::amendments()); -static Expected +static std::expected parseAMM( json::Value const& params, json::StaticString const fieldName, @@ -125,21 +125,21 @@ parseAMM( if (auto const value = LedgerEntryHelpers::hasRequired(params, {jss::asset, jss::asset2}); !value) { - return Unexpected(value.error()); + return std::unexpected(value.error()); } auto const asset = LedgerEntryHelpers::requiredAsset(params, jss::asset, "malformedRequest"); if (!asset) - return Unexpected(asset.error()); + return std::unexpected(asset.error()); auto const asset2 = LedgerEntryHelpers::requiredAsset(params, jss::asset2, "malformedRequest"); if (!asset2) - return Unexpected(asset2.error()); + return std::unexpected(asset2.error()); return keylet::amm(*asset, *asset2).key; } -static Expected +static std::expected parseBridge( json::Value const& params, json::StaticString const fieldName, @@ -147,7 +147,7 @@ parseBridge( { if (!params.isMember(jss::bridge)) { - return Unexpected(LedgerEntryHelpers::missingFieldError(jss::bridge)); + return std::unexpected(LedgerEntryHelpers::missingFieldError(jss::bridge)); } if (params[jss::bridge].isString()) @@ -157,12 +157,12 @@ parseBridge( auto const bridge = LedgerEntryHelpers::parseBridgeFields(params[jss::bridge]); if (!bridge) - return Unexpected(bridge.error()); + return std::unexpected(bridge.error()); auto const account = LedgerEntryHelpers::requiredAccountID( params, jss::bridge_account, "malformedBridgeAccount"); if (!account) - return Unexpected(account.error()); + return std::unexpected(account.error()); STXChainBridge::ChainType const chainType = STXChainBridge::srcChain(account.value() == bridge->lockingChainDoor()); @@ -172,7 +172,7 @@ parseBridge( return keylet::bridge(*bridge, chainType).key; } -static Expected +static std::expected parseCheck( json::Value const& params, json::StaticString const fieldName, @@ -181,7 +181,7 @@ parseCheck( return parseObjectID(params, fieldName, "hex string"); } -static Expected +static std::expected parseCredential( json::Value const& cred, json::StaticString const fieldName, @@ -195,22 +195,22 @@ parseCredential( auto const subject = LedgerEntryHelpers::requiredAccountID(cred, jss::subject, "malformedRequest"); if (!subject) - return Unexpected(subject.error()); + return std::unexpected(subject.error()); auto const issuer = LedgerEntryHelpers::requiredAccountID(cred, jss::issuer, "malformedRequest"); if (!issuer) - return Unexpected(issuer.error()); + return std::unexpected(issuer.error()); auto const credType = LedgerEntryHelpers::requiredHexBlob( cred, jss::credential_type, kMaxCredentialTypeLength, "malformedRequest"); if (!credType) - return Unexpected(credType.error()); + return std::unexpected(credType.error()); return keylet::credential(*subject, *issuer, Slice(credType->data(), credType->size())).key; } -static Expected +static std::expected parseDelegate( json::Value const& params, json::StaticString const fieldName, @@ -224,17 +224,17 @@ parseDelegate( auto const account = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress"); if (!account) - return Unexpected(account.error()); + return std::unexpected(account.error()); auto const authorize = LedgerEntryHelpers::requiredAccountID(params, jss::authorize, "malformedAddress"); if (!authorize) - return Unexpected(authorize.error()); + return std::unexpected(authorize.error()); return keylet::delegate(*account, *authorize).key; } -static Expected +static std::expected parseAuthorizeCredentials(json::Value const& jv) { if (!jv.isArray()) @@ -246,7 +246,7 @@ parseAuthorizeCredentials(json::Value const& jv) std::uint32_t const n = jv.size(); if (n > kMaxCredentialsArraySize) { - return Unexpected( + return std::unexpected( LedgerEntryHelpers::malformedError( "malformedAuthorizedCredentials", "Invalid field '" + std::string(jss::authorized_credentials) + @@ -255,7 +255,7 @@ parseAuthorizeCredentials(json::Value const& jv) if (n == 0) { - return Unexpected( + return std::unexpected( LedgerEntryHelpers::malformedError( "malformedAuthorizedCredentials", "Invalid field '" + std::string(jss::authorized_credentials) + "', array empty.")); @@ -274,18 +274,18 @@ parseAuthorizeCredentials(json::Value const& jv) jo, {jss::issuer, jss::credential_type}, "malformedAuthorizedCredentials"); !value) { - return Unexpected(value.error()); + return std::unexpected(value.error()); } auto const issuer = LedgerEntryHelpers::requiredAccountID( jo, jss::issuer, "malformedAuthorizedCredentials"); if (!issuer) - return Unexpected(issuer.error()); + return std::unexpected(issuer.error()); auto const credentialType = LedgerEntryHelpers::requiredHexBlob( jo, jss::credential_type, kMaxCredentialTypeLength, "malformedAuthorizedCredentials"); if (!credentialType) - return Unexpected(credentialType.error()); + return std::unexpected(credentialType.error()); auto credential = STObject::makeInnerObject(sfCredential); credential.setAccountID(sfIssuer, *issuer); @@ -296,7 +296,7 @@ parseAuthorizeCredentials(json::Value const& jv) return arr; } -static Expected +static std::expected parseDepositPreauth( json::Value const& dp, json::StaticString const fieldName, @@ -318,7 +318,7 @@ parseDepositPreauth( auto const owner = LedgerEntryHelpers::requiredAccountID(dp, jss::owner, "malformedOwner"); if (!owner) { - return Unexpected(owner.error()); + return std::unexpected(owner.error()); } if (dp.isMember(jss::authorized)) @@ -334,7 +334,7 @@ parseDepositPreauth( auto const& ac(dp[jss::authorized_credentials]); auto const arr = parseAuthorizeCredentials(ac); if (!arr.has_value()) - return Unexpected(arr.error()); + return std::unexpected(arr.error()); auto const& sorted = credentials::makeSorted(arr.value()); if (sorted.empty()) @@ -347,7 +347,7 @@ parseDepositPreauth( return keylet::depositPreauth(*owner, sorted).key; } -static Expected +static std::expected parseDID( json::Value const& params, json::StaticString const fieldName, @@ -362,7 +362,7 @@ parseDID( return keylet::did(*account).key; } -static Expected +static std::expected parseDirectoryNode( json::Value const& params, json::StaticString const fieldName, @@ -413,7 +413,7 @@ parseDirectoryNode( return LedgerEntryHelpers::malformedError("malformedRequest", ""); } -static Expected +static std::expected parseEscrow( json::Value const& params, json::StaticString const fieldName, @@ -426,17 +426,17 @@ parseEscrow( auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner"); if (!id) - return Unexpected(id.error()); + return std::unexpected(id.error()); auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq"); if (!seq) - return Unexpected(seq.error()); + return std::unexpected(seq.error()); return keylet::escrow(*id, *seq).key; } auto const parseFeeSettings = fixed(keylet::fees()); -static Expected +static std::expected parseFixed( Keylet const& keylet, json::Value const& params, @@ -455,7 +455,7 @@ parseFixed( return keylet.key; } -static Expected +static std::expected parseLedgerHashes( json::Value const& params, json::StaticString const fieldName, @@ -475,7 +475,7 @@ parseLedgerHashes( return parseFixed(keylet::skip(), params, fieldName, apiVersion); } -static Expected +static std::expected parseLoanBroker( json::Value const& params, json::StaticString const fieldName, @@ -488,15 +488,15 @@ parseLoanBroker( auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner"); if (!id) - return Unexpected(id.error()); + return std::unexpected(id.error()); auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq"); if (!seq) - return Unexpected(seq.error()); + return std::unexpected(seq.error()); return keylet::loanbroker(*id, *seq).key; } -static Expected +static std::expected parseLoan( json::Value const& params, json::StaticString const fieldName, @@ -510,15 +510,15 @@ parseLoan( auto const id = LedgerEntryHelpers::requiredUInt256(params, jss::loan_broker_id, "malformedBroker"); if (!id) - return Unexpected(id.error()); + return std::unexpected(id.error()); auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::loan_seq, "malformedSeq"); if (!seq) - return Unexpected(seq.error()); + return std::unexpected(seq.error()); return keylet::loan(*id, *seq).key; } -static Expected +static std::expected parseMPToken( json::Value const& params, json::StaticString const fieldName, @@ -532,17 +532,17 @@ parseMPToken( auto const mptIssuanceID = LedgerEntryHelpers::requiredUInt192(params, jss::mpt_issuance_id, "malformedMPTIssuanceID"); if (!mptIssuanceID) - return Unexpected(mptIssuanceID.error()); + return std::unexpected(mptIssuanceID.error()); auto const account = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount"); if (!account) - return Unexpected(account.error()); + return std::unexpected(account.error()); return keylet::mptoken(*mptIssuanceID, *account).key; } -static Expected +static std::expected parseMPTokenIssuance( json::Value const& params, json::StaticString const fieldName, @@ -558,7 +558,7 @@ parseMPTokenIssuance( return keylet::mptIssuance(*mptIssuanceID).key; } -static Expected +static std::expected parseNFTokenOffer( json::Value const& params, json::StaticString const fieldName, @@ -567,7 +567,7 @@ parseNFTokenOffer( return parseObjectID(params, fieldName, "hex string"); } -static Expected +static std::expected parseNFTokenPage( json::Value const& params, json::StaticString const fieldName, @@ -578,7 +578,7 @@ parseNFTokenPage( auto const parseNegativeUNL = fixed(keylet::negativeUNL()); -static Expected +static std::expected parseOffer( json::Value const& params, json::StaticString const fieldName, @@ -591,16 +591,16 @@ parseOffer( auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress"); if (!id) - return Unexpected(id.error()); + return std::unexpected(id.error()); auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest"); if (!seq) - return Unexpected(seq.error()); + return std::unexpected(seq.error()); return keylet::offer(*id, *seq).key; } -static Expected +static std::expected parseOracle( json::Value const& params, json::StaticString const fieldName, @@ -613,17 +613,17 @@ parseOracle( auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAccount"); if (!id) - return Unexpected(id.error()); + return std::unexpected(id.error()); auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::oracle_document_id, "malformedDocumentID"); if (!seq) - return Unexpected(seq.error()); + return std::unexpected(seq.error()); return keylet::oracle(*id, *seq).key; } -static Expected +static std::expected parsePayChannel( json::Value const& params, json::StaticString const fieldName, @@ -632,7 +632,7 @@ parsePayChannel( return parseObjectID(params, fieldName, "hex string"); } -static Expected +static std::expected parsePermissionedDomain( json::Value const& pd, json::StaticString const fieldName, @@ -652,16 +652,16 @@ parsePermissionedDomain( auto const account = LedgerEntryHelpers::requiredAccountID(pd, jss::account, "malformedAddress"); if (!account) - return Unexpected(account.error()); + return std::unexpected(account.error()); auto const seq = LedgerEntryHelpers::requiredUInt32(pd, jss::seq, "malformedRequest"); if (!seq) - return Unexpected(seq.error()); + return std::unexpected(seq.error()); return keylet::permissionedDomain(*account, pd[jss::seq].asUInt()).key; } -static Expected +static std::expected parseRippleState( json::Value const& jvRippleState, json::StaticString const fieldName, @@ -678,7 +678,7 @@ parseRippleState( LedgerEntryHelpers::hasRequired(jvRippleState, {jss::currency, jss::accounts}); !value) { - return Unexpected(value.error()); + return std::unexpected(value.error()); } if (!jvRippleState[jss::accounts].isArray() || jvRippleState[jss::accounts].size() != 2) @@ -710,7 +710,7 @@ parseRippleState( return keylet::line(*id1, *id2, uCurrency).key; } -static Expected +static std::expected parseSignerList( json::Value const& params, json::StaticString const fieldName, @@ -719,7 +719,7 @@ parseSignerList( return parseObjectID(params, fieldName, "hex string"); } -static Expected +static std::expected parseTicket( json::Value const& params, json::StaticString const fieldName, @@ -732,17 +732,17 @@ parseTicket( auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::account, "malformedAddress"); if (!id) - return Unexpected(id.error()); + return std::unexpected(id.error()); auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::ticket_seq, "malformedRequest"); if (!seq) - return Unexpected(seq.error()); + return std::unexpected(seq.error()); return getTicketIndex(*id, *seq); } -static Expected +static std::expected parseVault( json::Value const& params, json::StaticString const fieldName, @@ -755,16 +755,16 @@ parseVault( auto const id = LedgerEntryHelpers::requiredAccountID(params, jss::owner, "malformedOwner"); if (!id) - return Unexpected(id.error()); + return std::unexpected(id.error()); auto const seq = LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedRequest"); if (!seq) - return Unexpected(seq.error()); + return std::unexpected(seq.error()); return keylet::vault(*id, *seq).key; } -static Expected +static std::expected parseXChainOwnedClaimID( json::Value const& claimId, json::StaticString const fieldName, @@ -777,20 +777,20 @@ parseXChainOwnedClaimID( auto const bridgeSpec = LedgerEntryHelpers::parseBridgeFields(claimId); if (!bridgeSpec) - return Unexpected(bridgeSpec.error()); + return std::unexpected(bridgeSpec.error()); auto const seq = LedgerEntryHelpers::requiredUInt32( claimId, jss::xchain_owned_claim_id, "malformedXChainOwnedClaimID"); if (!seq) { - return Unexpected(seq.error()); + return std::unexpected(seq.error()); } - Keylet keylet = keylet::xChainClaimID(*bridgeSpec, *seq); + Keylet const keylet = keylet::xChainClaimID(*bridgeSpec, *seq); return keylet.key; } -static Expected +static std::expected parseXChainOwnedCreateAccountClaimID( json::Value const& claimId, json::StaticString const fieldName, @@ -803,7 +803,7 @@ parseXChainOwnedCreateAccountClaimID( auto const bridgeSpec = LedgerEntryHelpers::parseBridgeFields(claimId); if (!bridgeSpec) - return Unexpected(bridgeSpec.error()); + return std::unexpected(bridgeSpec.error()); auto const seq = LedgerEntryHelpers::requiredUInt32( claimId, @@ -811,10 +811,10 @@ parseXChainOwnedCreateAccountClaimID( "malformedXChainOwnedCreateAccountClaimID"); if (!seq) { - return Unexpected(seq.error()); + return std::unexpected(seq.error()); } - Keylet keylet = keylet::xChainCreateAccountClaimID(*bridgeSpec, *seq); + Keylet const keylet = keylet::xChainCreateAccountClaimID(*bridgeSpec, *seq); return keylet.key; } diff --git a/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h b/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h index e8e295ca2c..463547a90d 100644 --- a/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h +++ b/src/xrpld/rpc/handlers/ledger/LedgerEntryHelpers.h @@ -10,41 +10,42 @@ #include #include +#include #include namespace xrpl::LedgerEntryHelpers { -inline Unexpected +inline std::unexpected missingFieldError(json::StaticString const field, std::optional err = std::nullopt) { json::Value json = json::ValueType::Object; json[jss::error] = err.value_or("malformedRequest"); json[jss::error_code] = RpcInvalidParams; json[jss::error_message] = RPC::missingFieldMessage(std::string(field.cStr())); - return Unexpected(json); + return std::unexpected(json); } -inline Unexpected +inline std::unexpected invalidFieldError(std::string const& err, json::StaticString const field, std::string const& type) { json::Value json = json::ValueType::Object; json[jss::error] = err; json[jss::error_code] = RpcInvalidParams; json[jss::error_message] = RPC::expectedFieldMessage(field, type); - return Unexpected(json); + return std::unexpected(json); } -inline Unexpected +inline std::unexpected malformedError(std::string const& err, std::string const& message) { json::Value json = json::ValueType::Object; json[jss::error] = err; json[jss::error_code] = RpcInvalidParams; json[jss::error_message] = message; - return Unexpected(json); + return std::unexpected(json); } -inline Expected +inline std::expected hasRequired( json::Value const& params, std::initializer_list fields, @@ -65,7 +66,7 @@ std::optional parse(json::Value const& param); template -Expected +std::expected required( json::Value const& params, json::StaticString const fieldName, @@ -99,7 +100,7 @@ parse(json::Value const& param) return account; } -inline Expected +inline std::expected requiredAccountID( json::Value const& params, json::StaticString const fieldName, @@ -121,7 +122,7 @@ parseHexBlob(json::Value const& param, std::size_t maxLength) return blob; } -inline Expected +inline std::expected requiredHexBlob( json::Value const& params, json::StaticString const fieldName, @@ -156,7 +157,7 @@ parse(json::Value const& param) return std::nullopt; } -inline Expected +inline std::expected requiredUInt32( json::Value const& params, json::StaticString const fieldName, @@ -178,7 +179,7 @@ parse(json::Value const& param) return uNodeIndex; } -inline Expected +inline std::expected requiredUInt256( json::Value const& params, json::StaticString const fieldName, @@ -200,7 +201,7 @@ parse(json::Value const& param) return field; } -inline Expected +inline std::expected requiredUInt192( json::Value const& params, json::StaticString const fieldName, @@ -223,13 +224,13 @@ parse(json::Value const& param) } } -inline Expected +inline std::expected requiredAsset(json::Value const& params, json::StaticString const fieldName, std::string const& err) { return required(params, fieldName, err, "Asset"); } -inline Expected +inline std::expected parseBridgeFields(json::Value const& params) { if (auto const value = hasRequired( @@ -240,21 +241,21 @@ parseBridgeFields(json::Value const& params) jss::IssuingChainIssue}); !value) { - return Unexpected(value.error()); + return std::unexpected(value.error()); } auto const lockingChainDoor = requiredAccountID(params, jss::LockingChainDoor, "malformedLockingChainDoor"); if (!lockingChainDoor) { - return Unexpected(lockingChainDoor.error()); + return std::unexpected(lockingChainDoor.error()); } auto const issuingChainDoor = requiredAccountID(params, jss::IssuingChainDoor, "malformedIssuingChainDoor"); if (!issuingChainDoor) { - return Unexpected(issuingChainDoor.error()); + return std::unexpected(issuingChainDoor.error()); } Issue lockingChainIssue; diff --git a/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp index b9f4a42880..2c2f96b0e8 100644 --- a/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp +++ b/src/xrpld/rpc/handlers/orderbook/AMMInfo.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -18,7 +17,7 @@ #include #include #include -#include +#include // IWYU pragma: keep #include #include #include @@ -27,6 +26,7 @@ #include #include +#include #include #include #include @@ -35,7 +35,7 @@ namespace xrpl { -Expected +std::expected getAsset(json::Value const& v, beast::Journal j) { try @@ -46,7 +46,7 @@ getAsset(json::Value const& v, beast::Journal j) { JLOG(j.debug()) << "getAsset " << ex.what(); } - return Unexpected(RpcIssueMalformed); + return std::unexpected(RpcIssueMalformed); } std::string @@ -79,7 +79,7 @@ doAMMInfo(RPC::JsonContext& context) SLE::const_pointer amm; }; - auto getValuesFromContextParams = [&]() -> Expected { + auto getValuesFromContextParams = [&]() -> std::expected { std::optional accountID; std::optional asset1; std::optional asset2; @@ -92,7 +92,7 @@ doAMMInfo(RPC::JsonContext& context) // NOTE, identical check for apVersion >= 3 below if (context.apiVersion < 3 && kInvalid(params)) - return Unexpected(RpcInvalidParams); + return std::unexpected(RpcInvalidParams); if (params.isMember(jss::asset)) { @@ -102,7 +102,7 @@ doAMMInfo(RPC::JsonContext& context) } else { - return Unexpected(i.error()); + return std::unexpected(i.error()); } } @@ -114,7 +114,7 @@ doAMMInfo(RPC::JsonContext& context) } else { - return Unexpected(i.error()); + return std::unexpected(i.error()); } } @@ -122,25 +122,25 @@ doAMMInfo(RPC::JsonContext& context) { auto const id = parseBase58((params[jss::amm_account].asString())); if (!id) - return Unexpected(RpcActMalformed); + return std::unexpected(RpcActMalformed); auto const sle = ledger->read(keylet::account(*id)); if (!sle) - return Unexpected(RpcActMalformed); + return std::unexpected(RpcActMalformed); ammID = sle->getFieldH256(sfAMMID); if (ammID->isZero()) - return Unexpected(RpcActNotFound); + return std::unexpected(RpcActNotFound); } if (params.isMember(jss::account)) { accountID = parseBase58(params[jss::account].asString()); if (!accountID || !ledger->read(keylet::account(*accountID))) - return Unexpected(RpcActMalformed); + return std::unexpected(RpcActMalformed); } // NOTE, identical check for apVersion < 3 above if (context.apiVersion >= 3 && kInvalid(params)) - return Unexpected(RpcInvalidParams); + return std::unexpected(RpcInvalidParams); XRPL_ASSERT( (asset1.has_value() == asset2.has_value()) && (asset1.has_value() != ammID.has_value()), @@ -154,7 +154,7 @@ doAMMInfo(RPC::JsonContext& context) }(); auto const amm = ledger->read(ammKeylet); if (!amm) - return Unexpected(RpcActNotFound); + return std::unexpected(RpcActNotFound); if (!asset1 && !asset2) { asset1 = (*amm)[sfAsset]; diff --git a/src/xrpld/rpc/handlers/server_info/ServerDefinitions.cpp b/src/xrpld/rpc/handlers/server_info/ServerDefinitions.cpp index aa23a7af26..1e9e0ddc14 100644 --- a/src/xrpld/rpc/handlers/server_info/ServerDefinitions.cpp +++ b/src/xrpld/rpc/handlers/server_info/ServerDefinitions.cpp @@ -65,7 +65,7 @@ ServerDefinitions::translate(std::string const& inp) }; // TODO: use string::contains with C++23 - auto contains = [&](std::string_view s) -> bool { return inp.find(s) != std::string::npos; }; + auto contains = [&](std::string_view s) -> bool { return inp.contains(s); }; if (contains("UINT")) { diff --git a/src/xrpld/rpc/handlers/transaction/Simulate.cpp b/src/xrpld/rpc/handlers/transaction/Simulate.cpp index 676f0318a2..7ee28c4886 100644 --- a/src/xrpld/rpc/handlers/transaction/Simulate.cpp +++ b/src/xrpld/rpc/handlers/transaction/Simulate.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -33,6 +32,7 @@ #include #include +#include #include #include #include @@ -42,7 +42,7 @@ namespace xrpl { -static Expected +static std::expected getAutofillSequence(json::Value const& txJson, RPC::JsonContext& context) { // autofill Sequence @@ -52,13 +52,13 @@ getAutofillSequence(json::Value const& txJson, RPC::JsonContext& context) { // sanity check, should fail earlier // LCOV_EXCL_START - return Unexpected(RPC::invalidFieldError("tx.Account")); + return std::unexpected(RPC::invalidFieldError("tx.Account")); // LCOV_EXCL_STOP } auto const srcAddressID = parseBase58(accountStr.asString()); if (!srcAddressID.has_value()) { - return Unexpected( + return std::unexpected( RPC::makeError(RpcSrcActMalformed, RPC::invalidFieldMessage("tx.Account"))); } SLE::const_pointer const sle = @@ -69,7 +69,7 @@ getAutofillSequence(json::Value const& txJson, RPC::JsonContext& context) << "Failed to find source account " << "in current ledger: " << toBase58(*srcAddressID); - return Unexpected(rpcError(RpcSrcActNotFound)); + return std::unexpected(rpcError(RpcSrcActNotFound)); } return hasTicketSeq ? 0 : context.app.getTxQ().nextQueuableSeq(sle).value(); diff --git a/src/xrpld/rpc/handlers/transaction/Submit.cpp b/src/xrpld/rpc/handlers/transaction/Submit.cpp index f0c4cb2391..79f3680684 100644 --- a/src/xrpld/rpc/handlers/transaction/Submit.cpp +++ b/src/xrpld/rpc/handlers/transaction/Submit.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -21,17 +20,18 @@ #include #include +#include #include #include namespace xrpl { -static Expected +static std::expected getFailHard(RPC::JsonContext const& context) { if (context.params.isMember(jss::fail_hard) && !context.params[jss::fail_hard].isBool()) { - return Unexpected(RPC::expectedFieldError(jss::fail_hard, "boolean")); + return std::unexpected(RPC::expectedFieldError(jss::fail_hard, "boolean")); } return NetworkOPs::doFailHard( context.params.isMember(jss::fail_hard) && context.params[jss::fail_hard].asBool()); From 2cbc3c139e0469c98e49d4fe9453de2d15f6f4e4 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Tue, 9 Jun 2026 13:46:56 -0400 Subject: [PATCH 76/87] fix: Fix Number comparison operator (#7406) --- include/xrpl/basics/Number.h | 35 ++++++----- src/test/basics/Number_test.cpp | 104 ++++++++++++++++++++++++++++++-- 2 files changed, 121 insertions(+), 18 deletions(-) diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index 93bef82a8c..cee0c45355 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -408,33 +408,40 @@ public: } friend constexpr bool - operator<(Number const& x, Number const& y) noexcept + operator<(Number const& l, Number const& r) noexcept { + bool const lneg = l.negative_; + bool const rneg = r.negative_; + // If the two amounts have different signs (zero is treated as positive) // then the comparison is true iff the left is negative. - bool const lneg = x.negative_; - bool const rneg = y.negative_; - if (lneg != rneg) return lneg; - // Both have same sign and the left is zero: the right must be - // greater than 0. - if (x.mantissa_ == 0) - return y.mantissa_ > 0; + // Both have same sign and the left is zero: both must be non-negative. + // If the right is greater than 0, then it is larger, so the comparison is true. + if (l.mantissa_ == 0) + return r.mantissa_ > 0; - // Both have same sign, the right is zero and the left is non-zero. - if (y.mantissa_ == 0) + // Both have same sign, the right is zero and the left is non-zero, so the left must be + // positive, and thus is larger, so the comparison is false. + if (r.mantissa_ == 0) return false; // Both have the same sign, compare by exponents: - if (x.exponent_ > y.exponent_) + if (l.exponent_ > r.exponent_) return lneg; - if (x.exponent_ < y.exponent_) + if (l.exponent_ < r.exponent_) return !lneg; - // If equal exponents, compare mantissas - return x.mantissa_ < y.mantissa_; + // If equal signs and exponents, compare mantissas. + if (lneg) + { + // If negative, the operator is reversed. + return l.mantissa_ > r.mantissa_; + } + + return l.mantissa_ < r.mantissa_; } /** Return the sign of the amount */ diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 81019970ad..f4bd1c9d66 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,8 @@ #include #include #include +#include +#include namespace xrpl { @@ -1386,10 +1389,103 @@ public: testRelationals() { testcase << "test_relationals " << to_string(Number::getMantissaScale()); - BEAST_EXPECT(!(Number{100} < Number{10})); - BEAST_EXPECT(Number{100} > Number{10}); - BEAST_EXPECT(Number{100} >= Number{10}); - BEAST_EXPECT(!(Number{100} <= Number{10})); + + { + auto test = [this](auto const& nums) { + BEAST_EXPECT(std::ranges::is_sorted(nums)); + + for (auto iter1 = nums.begin(); iter1 != nums.end(); ++iter1) + { + auto iter2 = iter1; + for (++iter2; iter2 != nums.end(); ++iter2) + { + Number const& smaller = *iter1; + Number const& larger = *iter2; + std::stringstream ss; + ss << smaller << " < " << larger; + auto const str = ss.str(); + + // The ==/!= operators use a completely different code path than <, etc. + // This helps detect a breakage in one but not the other. It also helps + // verify that the values are being ordered correctly. + BEAST_EXPECTS(smaller != larger, str + " (!=)"); + BEAST_EXPECTS(!(smaller == larger), str + " (==)"); + + // true results using operator< and derived operators + BEAST_EXPECTS(smaller < larger, str + " (<)"); + BEAST_EXPECTS(larger > smaller, str + " (>)"); + BEAST_EXPECTS(larger >= smaller, str + " (>=)"); + BEAST_EXPECTS(smaller <= larger, str + " (<=)"); + + // false results using operator< and derived operators + BEAST_EXPECTS(!(larger < smaller), str + " (! <)"); + BEAST_EXPECTS(!(smaller > larger), str + " (! >)"); + BEAST_EXPECTS(!(smaller >= larger), str + " (! >=)"); + BEAST_EXPECTS(!(larger <= smaller), str + " (! <=)"); + } + } + }; + + auto const intNums = [this]() { + // Inequality test cases are built from a list of sorted integers + auto const values = + std::to_array({-100, -50, -20, -10, -1, 0, 1, 10, 20, 50, 100}); + // Check this list is sorted before converting it to Numbers. + // That way if any of the other tests fail, we know it's because of code and not the + // source data. + BEAST_EXPECT(std::ranges::is_sorted(values)); + + std::vector result; + result.reserve(values.size()); + for (auto const v : values) + result.emplace_back(v); + return result; + }(); + + auto const otherNums = std::to_array({ + Number{-5, 100}, + Number{-1, 100}, + Number{-7, -10}, + Number{-2, -10}, + Number{0}, + Number{2, -10}, + Number{7, -10}, + Number{1, 100}, + Number{5, 100}, + }); + + test(intNums); + test(otherNums); + } + + { + // Equality test cases are . Number will be compared against itself + using Case = std::pair; + auto const c = std::to_array({ + {700, __LINE__}, + {50, __LINE__}, + {1, __LINE__}, + {0, __LINE__}, + {-1, __LINE__}, + {-30, __LINE__}, + {-600, __LINE__}, + }); + for (auto const& [n, line] : c) + { + auto const str = to_string(n); + + // NOLINTBEGIN(misc-redundant-expression) Explicitly testing operators with + // equivalent values + expect(n == n, str + " ==", __FILE__, line); + expect(!(n != n), str + " !=", __FILE__, line); + + expect(!(n < n), str + " < ", __FILE__, line); + expect(!(n > n), str + " >", __FILE__, line); + expect(n >= n, str + " >=", __FILE__, line); + expect(n <= n, str + " <=", __FILE__, line); + // NOLINTEND(misc-redundant-expression) + } + } } void From 8617eaeb26f07e7c671b8f86fb639179a5998f71 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 10 Jun 2026 01:00:19 +0100 Subject: [PATCH 77/87] ci: Launch upload-conan-deps on profile change (#7442) --- .github/workflows/upload-conan-deps.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index 1a52ceee63..7ca9d13007 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -30,6 +30,7 @@ on: - ".github/scripts/strategy-matrix/**" - conanfile.py - conan.lock + - conan/profiles/** env: CONAN_REMOTE_NAME: xrplf From 742aa0878bbccdce385db4c8b6b77f2da19fad4a Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 10 Jun 2026 05:16:53 -0400 Subject: [PATCH 78/87] test: Do not create data directory for memory databases (#7323) Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com> --- src/xrpld/app/misc/SHAMapStoreImp.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/xrpld/app/misc/SHAMapStoreImp.cpp b/src/xrpld/app/misc/SHAMapStoreImp.cpp index 7259c233e4..33bbf1c613 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.cpp +++ b/src/xrpld/app/misc/SHAMapStoreImp.cpp @@ -387,8 +387,12 @@ void SHAMapStoreImp::dbPaths() { Section const section{app_.config().section(Sections::kNodeDatabase)}; - boost::filesystem::path dbPath = get(section, Keys::kPath); + // Skip creating the directory when an in-memory database is used. + if (boost::iequals(get(section, Keys::kType), "memory")) + return; + + boost::filesystem::path dbPath = get(section, Keys::kPath); if (boost::filesystem::exists(dbPath)) { if (!boost::filesystem::is_directory(dbPath)) From 8a4bf2dee60e4082b584c1702eabf93225f8da40 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Wed, 10 Jun 2026 11:16:03 +0100 Subject: [PATCH 79/87] refactor: Retire fixUniversalNumber amendment (#5962) Signed-off-by: Pratik Mankawde Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) --- include/xrpl/protocol/AMMCore.h | 2 +- include/xrpl/protocol/IOUAmount.h | 33 ---- include/xrpl/protocol/Rules.h | 2 - include/xrpl/protocol/detail/features.macro | 2 +- src/libxrpl/protocol/AMMCore.cpp | 2 +- src/libxrpl/protocol/IOUAmount.cpp | 104 +---------- src/libxrpl/protocol/Rules.cpp | 4 - src/libxrpl/protocol/STAmount.cpp | 188 +++----------------- src/libxrpl/tx/Transactor.cpp | 3 - src/libxrpl/tx/apply.cpp | 2 - src/libxrpl/tx/applySteps.cpp | 4 +- src/test/app/AMM_test.cpp | 7 +- src/test/app/NFToken_test.cpp | 14 +- src/test/app/OfferMPT_test.cpp | 1 - src/test/app/Offer_test.cpp | 60 +++---- src/test/basics/Number_test.cpp | 1 - src/xrpld/app/misc/detail/TxQ.cpp | 5 - 17 files changed, 55 insertions(+), 379 deletions(-) diff --git a/include/xrpl/protocol/AMMCore.h b/include/xrpl/protocol/AMMCore.h index ced84c4c87..a83c8bfa84 100644 --- a/include/xrpl/protocol/AMMCore.h +++ b/include/xrpl/protocol/AMMCore.h @@ -65,7 +65,7 @@ invalidAMMAssetPair( std::optional ammAuctionTimeSlot(std::uint64_t current, STObject const& auctionSlot); -/** Return true if required AMM amendments are enabled +/** Return true if required AMM amendment is enabled */ bool ammEnabled(Rules const&); diff --git a/include/xrpl/protocol/IOUAmount.h b/include/xrpl/protocol/IOUAmount.h index 9e0fbe38eb..b057f1c245 100644 --- a/include/xrpl/protocol/IOUAmount.h +++ b/include/xrpl/protocol/IOUAmount.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -179,36 +178,4 @@ to_string(IOUAmount const& amount); IOUAmount mulRatio(IOUAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp); -// Since many uses of the number class do not have access to a ledger, -// getSTNumberSwitchover needs to be globally accessible. - -bool -getSTNumberSwitchover(); - -void -setSTNumberSwitchover(bool v); - -/** RAII class to set and restore the Number switchover. - */ - -class NumberSO -{ - bool saved_; - -public: - ~NumberSO() - { - setSTNumberSwitchover(saved_); - } - - NumberSO(NumberSO const&) = delete; - NumberSO& - operator=(NumberSO const&) = delete; - - explicit NumberSO(bool v) : saved_(getSTNumberSwitchover()) - { - setSTNumberSwitchover(v); - } -}; - } // namespace xrpl diff --git a/include/xrpl/protocol/Rules.h b/include/xrpl/protocol/Rules.h index 9c17ff2391..47b20756db 100644 --- a/include/xrpl/protocol/Rules.h +++ b/include/xrpl/protocol/Rules.h @@ -122,7 +122,6 @@ private: std::optional saved_; }; -class NumberSO; class NumberMantissaScaleGuard; bool @@ -131,7 +130,6 @@ useRulesGuards(Rules const& rules); void createGuards( Rules const& rules, - std::optional& stNumberSO, std::optional& rulesGuard, std::optional& mantissaScaleGuard); diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index c99c1e5ce8..2b2f24ba53 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -64,7 +64,6 @@ XRPL_FIX (DisallowIncomingV1, Supported::Yes, VoteBehavior::DefaultNo XRPL_FEATURE(XChainBridge, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FEATURE(AMM, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FEATURE(Clawback, Supported::Yes, VoteBehavior::DefaultNo) -XRPL_FIX (UniversalNumber, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FEATURE(XRPFees, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FIX (RemoveNFTokenAutoTrustLine, Supported::Yes, VoteBehavior::DefaultYes) @@ -112,6 +111,7 @@ XRPL_RETIRE_FIX(RmSmallIncreasedQOffers) XRPL_RETIRE_FIX(STAmountCanonicalize) XRPL_RETIRE_FIX(TakerDryOfferRemoval) XRPL_RETIRE_FIX(TrustLinesToSelf) +XRPL_RETIRE_FIX(UniversalNumber) XRPL_RETIRE_FEATURE(Checks) XRPL_RETIRE_FEATURE(CheckCashMakesTrustLine) diff --git a/src/libxrpl/protocol/AMMCore.cpp b/src/libxrpl/protocol/AMMCore.cpp index eccb581c6d..e58ab29257 100644 --- a/src/libxrpl/protocol/AMMCore.cpp +++ b/src/libxrpl/protocol/AMMCore.cpp @@ -127,7 +127,7 @@ ammAuctionTimeSlot(std::uint64_t current, STObject const& auctionSlot) bool ammEnabled(Rules const& rules) { - return rules.enabled(featureAMM) && rules.enabled(fixUniversalNumber); + return rules.enabled(featureAMM); } } // namespace xrpl diff --git a/src/libxrpl/protocol/IOUAmount.cpp b/src/libxrpl/protocol/IOUAmount.cpp index d214995809..acbf6724e1 100644 --- a/src/libxrpl/protocol/IOUAmount.cpp +++ b/src/libxrpl/protocol/IOUAmount.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -17,29 +16,6 @@ namespace xrpl { -namespace { - -// Use a static inside a function to help prevent order-of-initialization issues -LocalValue& -getStaticSTNumberSwitchover() -{ - static LocalValue kR{true}; - return kR; -} -} // namespace - -bool -getSTNumberSwitchover() -{ - return *getStaticSTNumberSwitchover(); -} - -void -setSTNumberSwitchover(bool v) -{ - *getStaticSTNumberSwitchover() = v; -} - /* The range for the mantissa when normalized */ // log(2^63,10) ~ 18.96 // @@ -75,56 +51,20 @@ IOUAmount::normalize() return; } - if (getSTNumberSwitchover()) - { - Number const v{mantissa_, exponent_}; - *this = fromNumber(v); - if (exponent_ > kMaxExponent) - Throw("value overflow"); - if (exponent_ < kMinExponent) - *this = beast::kZero; - return; - } - - bool const negative = (mantissa_ < 0); - - if (negative) - mantissa_ = -mantissa_; - - while ((mantissa_ < kMinMantissa) && (exponent_ > kMinExponent)) - { - mantissa_ *= 10; - --exponent_; - } - - while (mantissa_ > kMaxMantissa) - { - if (exponent_ >= kMaxExponent) - Throw("IOUAmount::normalize"); - - mantissa_ /= 10; - ++exponent_; - } - - if ((exponent_ < kMinExponent) || (mantissa_ < kMinMantissa)) - { - *this = beast::kZero; - return; - } - - if (exponent_ > kMaxExponent) - Throw("value overflow"); - - if (negative) - mantissa_ = -mantissa_; + Number const v{mantissa_, exponent_}; + *this = IOUAmount(v); } IOUAmount::IOUAmount(Number const& other) : IOUAmount(fromNumber(other)) { if (exponent_ > kMaxExponent) + { Throw("value overflow"); + } if (exponent_ < kMinExponent) + { *this = beast::kZero; + } } IOUAmount& @@ -139,37 +79,7 @@ IOUAmount::operator+=(IOUAmount const& other) return *this; } - if (getSTNumberSwitchover()) - { - *this = IOUAmount{Number{*this} + Number{other}}; - return *this; - } - auto m = other.mantissa_; - auto e = other.exponent_; - - while (exponent_ < e) - { - mantissa_ /= 10; - ++exponent_; - } - - while (e < exponent_) - { - m /= 10; - ++e; - } - - // This addition cannot overflow an std::int64_t but we may throw from - // normalize if the result isn't representable. - mantissa_ += m; - - if (mantissa_ >= -10 && mantissa_ <= 10) - { - *this = beast::kZero; - return *this; - } - - normalize(); + *this = IOUAmount{Number{*this} + Number{other}}; return *this; } diff --git a/src/libxrpl/protocol/Rules.cpp b/src/libxrpl/protocol/Rules.cpp index 08a95145eb..e0968ea868 100644 --- a/src/libxrpl/protocol/Rules.cpp +++ b/src/libxrpl/protocol/Rules.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -83,15 +82,12 @@ useRulesGuards(Rules const& rules) void createGuards( Rules const& rules, - std::optional& stNumberSO, std::optional& rulesGuard, std::optional& mantissaScaleGuard) { if (useRulesGuards(rules)) { // raii classes for the current ledger rules. - // fixUniversalNumber predates the rulesGuard and should be replaced. - stNumberSO.emplace(rules.enabled(fixUniversalNumber)); rulesGuard.emplace(rules); } else diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 1ba9cd042f..449efc9543 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -388,47 +388,9 @@ operator+(STAmount const& v1, STAmount const& v2) if (v1.holds()) return {v1.asset_, v1.mpt().value() + v2.mpt().value()}; - if (getSTNumberSwitchover()) - { - auto x = v1; - x = v1.iou() + v2.iou(); - return x; - } - - int ov1 = v1.exponent(), ov2 = v2.exponent(); - std::int64_t vv1 = static_cast(v1.mantissa()); - std::int64_t vv2 = static_cast(v2.mantissa()); - - if (v1.negative()) - vv1 = -vv1; - - if (v2.negative()) - vv2 = -vv2; - - while (ov1 < ov2) - { - vv1 /= 10; - ++ov1; - } - - while (ov2 < ov1) - { - vv2 /= 10; - ++ov2; - } - - // This addition cannot overflow an std::int64_t. It can overflow an - // STAmount and the constructor will throw. - - std::int64_t const fv = vv1 + vv2; - - if ((fv >= -10) && (fv <= 10)) - return {v1.getFName(), v1.asset()}; - - if (fv >= 0) - return STAmount{v1.getFName(), v1.asset(), static_cast(fv), ov1, false}; - - return STAmount{v1.getFName(), v1.asset(), static_cast(-fv), ov1, true}; + auto x = v1; + x = v1.iou() + v2.iou(); + return x; } STAmount @@ -877,53 +839,25 @@ STAmount::canonicalize() if (asset_.holds() && offset_ > 18) Throw("MPT amount out of range"); - if (getSTNumberSwitchover()) + Number const num(isNegative_, value_, offset_, Number::Unchecked{}); + auto set = [&](auto const& val) { + auto const value = val.value(); + isNegative_ = value < 0; + value_ = isNegative_ ? -value : value; + }; + if (native()) { - Number const num(isNegative_, value_, offset_, Number::Unchecked{}); - auto set = [&](auto const& val) { - auto const value = val.value(); - isNegative_ = value < 0; - value_ = isNegative_ ? -value : value; - }; - if (native()) - { - set(XRPAmount{num}); - } - else if (asset_.holds()) - { - set(MPTAmount{num}); - } - else - { - Throw("Unknown integral asset type"); - } - offset_ = 0; + set(XRPAmount{num}); + } + else if (asset_.holds()) + { + set(MPTAmount{num}); } else { - while (offset_ < 0) - { - value_ /= 10; - ++offset_; - } - - while (offset_ > 0) - { - // N.B. do not move the overflow check to after the - // multiplication - if (native() && value_ > kMaxNativeN) - { - Throw("Native currency amount out of range"); - } - else if (!native() && value_ > kMaxMpTokenAmount) - { - Throw("MPT amount out of range"); - } - - value_ *= 10; - --offset_; - } + Throw("Unknown integral asset type"); // LCOV_EXCL_LINE } + offset_ = 0; if (native() && value_ > kMaxNativeN) { @@ -937,53 +871,7 @@ STAmount::canonicalize() return; } - if (getSTNumberSwitchover()) - { - *this = iou(); - return; - } - - if (value_ == 0) - { - offset_ = -100; - isNegative_ = false; - return; - } - - while ((value_ < kMinValue) && (offset_ > kMinOffset)) - { - value_ *= 10; - --offset_; - } - - while (value_ > kMaxValue) - { - if (offset_ >= kMaxOffset) - Throw("value overflow"); - - value_ /= 10; - ++offset_; - } - - if ((offset_ < kMinOffset) || (value_ < kMinValue)) - { - value_ = 0; - isNegative_ = false; - offset_ = -100; - return; - } - - if (offset_ > kMaxOffset) - Throw("value overflow"); - - XRPL_ASSERT( - (value_ == 0) || ((value_ >= kMinValue) && (value_ <= kMaxValue)), - "xrpl::STAmount::canonicalize : value inside range"); - XRPL_ASSERT( - (value_ == 0) || ((offset_ >= kMinOffset) && (offset_ <= kMaxOffset)), - "xrpl::STAmount::canonicalize : offset inside range"); - XRPL_ASSERT( - (value_ != 0) || (offset_ != -100), "xrpl::STAmount::canonicalize : value or offset set"); + *this = iou(); } void @@ -1395,44 +1283,8 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset) return STAmount(asset, minV * maxV); } - if (getSTNumberSwitchover()) - { - auto const r = Number{v1} * Number{v2}; - return STAmount{asset, r}; - } - - std::uint64_t value1 = v1.mantissa(); - std::uint64_t value2 = v2.mantissa(); - int offset1 = v1.exponent(); - int offset2 = v2.exponent(); - - if (v1.integral()) - { - while (value1 < STAmount::kMinValue) - { - value1 *= 10; - --offset1; - } - } - - if (v2.integral()) - { - while (value2 < STAmount::kMinValue) - { - value2 *= 10; - --offset2; - } - } - - // We multiply the two mantissas (each is between 10^15 - // and 10^16), so their product is in the 10^30 to 10^32 - // range. Dividing their product by 10^14 maintains the - // precision, by scaling the result to 10^16 to 10^18. - return STAmount( - asset, - muldiv(value1, value2, kTenTO14) + 7, - offset1 + offset2 + 14, - v1.negative() != v2.negative()); + auto const r = Number{v1} * Number{v2}; + return STAmount{asset, r}; } // This is the legacy version of canonicalizeRound. It's been in use diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 51541cc2e3..68d3f36916 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -1199,8 +1198,6 @@ Transactor::operator()() // with_txn_type(). // // raii classes for the current ledger rules. - // fixUniversalNumber predate the rulesGuard and should be replaced. - NumberSO const stNumberSO{view().rules().enabled(fixUniversalNumber)}; CurrentTransactionRulesGuard const currentTransactionRulesGuard(view().rules()); #ifdef DEBUG diff --git a/src/libxrpl/tx/apply.cpp b/src/libxrpl/tx/apply.cpp index 0fc0275eb0..b70cb0d345 100644 --- a/src/libxrpl/tx/apply.cpp +++ b/src/libxrpl/tx/apply.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -133,7 +132,6 @@ template ApplyResult apply(ServiceRegistry& registry, OpenView& view, PreflightChecks&& preflightChecks) { - NumberSO const stNumberSO{view.rules().enabled(fixUniversalNumber)}; return doApply(preclaim(preflightChecks(), registry, view), registry, view); } diff --git a/src/libxrpl/tx/applySteps.cpp b/src/libxrpl/tx/applySteps.cpp index 217fdd717f..336bb2004b 100644 --- a/src/libxrpl/tx/applySteps.cpp +++ b/src/libxrpl/tx/applySteps.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -70,10 +69,9 @@ withTxnType(Rules const& rules, TxType txnType, F&& f) // // See also Transactor::operator(). // - std::optional stNumberSO; std::optional rulesGuard; std::optional mantissaScaleGuard; - createGuards(rules, stNumberSO, rulesGuard, mantissaScaleGuard); + createGuards(rules, rulesGuard, mantissaScaleGuard); switch (txnType) { diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index 64972c24ab..e3a1cc935f 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -4333,15 +4333,10 @@ private: testAmendment() { testcase("Amendment"); - FeatureBitset const all{testableAmendments()}; - FeatureBitset const noAMM{all - featureAMM}; - FeatureBitset const noNumber{all - fixUniversalNumber}; - FeatureBitset const noAMMAndNumber{all - featureAMM - fixUniversalNumber}; using namespace jtx; + Env env{*this, testableAmendments() - featureAMM}; - for (auto const& feature : {noAMM, noNumber, noAMMAndNumber}) { - Env env{*this, feature}; fund(env, gw_, {alice_}, {USD(1'000)}, Fund::All); AMM amm(env, alice_, XRP(1'000), USD(1'000), Ter(temDISABLED)); diff --git a/src/test/app/NFToken_test.cpp b/src/test/app/NFToken_test.cpp index 269bc72c53..ba8f09c449 100644 --- a/src/test/app/NFToken_test.cpp +++ b/src/test/app/NFToken_test.cpp @@ -2236,17 +2236,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::Suite // See the impact of rounding when the nft is sold for small amounts // of drops. - for (auto numberSwitchOver : {true}) { - if (numberSwitchOver) - { - env.enableFeature(fixUniversalNumber); - } - else - { - env.disableFeature(fixUniversalNumber); - } - // An nft with a transfer fee of 1 basis point. uint256 const nftID = token::getNextID(env, alice, 0u, tfTransferable, 1); env(token::mint(alice), Txflags(tfTransferable), token::XferFee(1)); @@ -2268,7 +2258,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::Suite // minter sells to carol. The payment is just small enough that // alice does not get any transfer fee. - auto pmt = numberSwitchOver ? drops(50000) : drops(99999); + auto pmt = drops(50000); STAmount carolBalance = env.balance(carol); uint256 const minterSellOfferIndex = keylet::nftoffer(minter, env.seq(minter)).key; env(token::createOffer(minter, nftID, pmt), Txflags(tfSellNFToken)); @@ -2285,7 +2275,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::Suite // transfer that enables a transfer fee of 1 basis point. STAmount beckyBalance = env.balance(becky); uint256 const beckyBuyOfferIndex = keylet::nftoffer(becky, env.seq(becky)).key; - pmt = numberSwitchOver ? drops(50001) : drops(100000); + pmt = drops(50001); env(token::createOffer(becky, nftID, pmt), token::Owner(carol)); env.close(); env(token::acceptBuyOffer(carol, beckyBuyOfferIndex)); diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index e0f2f4eab0..ed0b2ffbe3 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -1827,7 +1827,6 @@ public: using namespace jtx; Env env{*this, features}; - env.enableFeature(fixUniversalNumber); auto const gw = Account{"gateway"}; auto const alice = Account{"alice"}; diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index 83c58884e0..7382f4f090 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -1973,54 +1973,36 @@ public: using namespace jtx; - for (auto numberSwitchOver : {false, true}) - { - Env env{*this, features}; - if (numberSwitchOver) - { - env.enableFeature(fixUniversalNumber); - } - else - { - env.disableFeature(fixUniversalNumber); - } + Env env{*this, features}; - auto const gw = Account{"gateway"}; - auto const alice = Account{"alice"}; - auto const bob = Account{"bob"}; - auto const usd = gw["USD"]; + auto const gw = Account{"gateway"}; + auto const alice = Account{"alice"}; + auto const bob = Account{"bob"}; + auto const usd = gw["USD"]; - env.fund(XRP(10000), gw, alice, bob); - env.close(); + env.fund(XRP(10000), gw, alice, bob); + env.close(); - env(rate(gw, 1.005)); + env(rate(gw, 1.005)); - env(trust(alice, usd(1000))); - env(trust(bob, usd(1000))); - env(trust(gw, alice["USD"](50))); + env(trust(alice, usd(1000))); + env(trust(bob, usd(1000))); + env(trust(gw, alice["USD"](50))); - env(pay(gw, bob, bob["USD"](1))); - env(pay(alice, gw, usd(50))); + env(pay(gw, bob, bob["USD"](1))); + env(pay(alice, gw, usd(50))); - env(trust(gw, alice["USD"](0))); + env(trust(gw, alice["USD"](0))); - env(offer(alice, usd(50), XRP(150000))); - env(offer(bob, XRP(100), usd(0.1))); + env(offer(alice, usd(50), XRP(150000))); + env(offer(bob, XRP(100), usd(0.1))); - auto jrr = ledgerEntryState(env, alice, gw, "USD"); - BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "49.96666666666667"); + auto jrr = ledgerEntryState(env, alice, gw, "USD"); + BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "49.96666666666667"); - jrr = ledgerEntryState(env, bob, gw, "USD"); - json::Value const bobUSD = jrr[jss::node][sfBalance.fieldName][jss::value]; - if (!numberSwitchOver) - { - BEAST_EXPECT(bobUSD == "-0.966500000033334"); - } - else - { - BEAST_EXPECT(bobUSD == "-0.9665000000333333"); - } - } + jrr = ledgerEntryState(env, bob, gw, "USD"); + json::Value const bobUSD = jrr[jss::node][sfBalance.fieldName][jss::value]; + BEAST_EXPECT(bobUSD == "-0.9665000000333333"); } void diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index f4bd1c9d66..2d2745de14 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -1514,7 +1514,6 @@ public: void testToStAmount() { - NumberSO const stNumberSO{true}; Issue const issue; Number const n{7'518'783'80596, -5}; SaveNumberRoundMode const save{Number::setround(Number::RoundingMode::ToNearest)}; diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index f98580ede4..0326828a70 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -16,8 +16,6 @@ #include #include #include -#include -#include #include #include #include @@ -306,7 +304,6 @@ TxQ::MaybeTx::apply(Application& app, OpenView& view, beast::Journal j) { // If the rules or flags change, preflight again XRPL_ASSERT(pfResult, "xrpl::TxQ::MaybeTx::apply : preflight result is set"); - NumberSO const stNumberSO{view.rules().enabled(fixUniversalNumber)}; // NOLINTBEGIN(bugprone-unchecked-optional-access) assert above if (pfResult->rules != view.rules() || pfResult->flags != flags) @@ -731,8 +728,6 @@ TxQ::apply( ApplyFlags flags, beast::Journal j) { - NumberSO const stNumberSO{view.rules().enabled(fixUniversalNumber)}; - // See if the transaction is valid, properly formed, // etc. before doing potentially expensive queue // replace and multi-transaction operations. From 97ca7d57bcedab341e7887cd7950299d2bde9598 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Wed, 10 Jun 2026 13:44:57 +0200 Subject: [PATCH 80/87] perf: Dispatch "hasInvalidAmount()" on type tag instead of dynamic_cast (#7402) --- src/libxrpl/protocol/STAmount.cpp | 32 ++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 449efc9543..748d00f25a 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -1138,16 +1138,34 @@ hasInvalidAmount(STBase const& field, int depth, beast::Journal j) return true; } - if (auto const amount = dynamic_cast(&field)) - return !isLegalMPT(*amount) || !isLegalNet(*amount); + // Dispatch on the serialized type tag rather than RTTI: this is on the invariant-checking path + // and a dynamic_cast chain over every field of every modified entry is measurably expensive. + // The object-like tags below all denote STObject subclasses (STLedgerEntry, STTx), so the + // downcast is sound; nested fields are only ever plain STI_OBJECT / STI_ARRAY containers. + // safeDowncast keeps a dynamic_cast validity assert in debug builds while compiling to + // static_cast in release. + switch (field.getSType()) + { + case STI_AMOUNT: { + auto const& amount = safeDowncast(field); + return !isLegalMPT(amount) || !isLegalNet(amount); + } - if (auto const object = dynamic_cast(&field)) - return hasInvalidAmount(*object, depth + 1, j); + case STI_OBJECT: + case STI_LEDGERENTRY: + case STI_TRANSACTION: + return hasInvalidAmount(safeDowncast(field), depth + 1, j); - if (auto const array = dynamic_cast(&field)) - return hasInvalidAmount(*array, depth + 1, j); + case STI_ARRAY: + return hasInvalidAmount(safeDowncast(field), depth + 1, j); - return false; + default: { + XRPL_ASSERT( + dynamic_cast(&field) == nullptr, + "xrpl::hasInvalidAmount : unhandled STObject type"); + return false; + } + } } bool From 83cc5df72e9fa2f4d9d91a674908afcd6a50302a Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Wed, 10 Jun 2026 14:05:53 +0200 Subject: [PATCH 81/87] fix: Disable transaction invariants (#7409) --- src/libxrpl/tx/Transactor.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 68d3f36916..aa7b81c015 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -1171,21 +1171,17 @@ Transactor::checkTransactionInvariants(TER result, XRPAmount fee) [[nodiscard]] TER Transactor::checkInvariants(TER result, XRPAmount fee) { - // Transaction invariants first (more specific). These check post-conditions of the specific - // transaction. If these fail, the transaction's core logic is wrong. - auto const txResult = checkTransactionInvariants(result, fee); - - // Protocol invariants second (broader). These check properties that must hold regardless of - // transaction type. - auto const protoResult = ctx_.checkInvariants(result, fee); - - // Fail if either check failed. tef (fatal) takes priority over tec. - if (protoResult == tefINVARIANT_FAILED) - return tefINVARIANT_FAILED; - if (txResult == tecINVARIANT_FAILED || protoResult == tecINVARIANT_FAILED) - return tecINVARIANT_FAILED; - - return result; + /* + * DISABLED for 3.2.0 — Must be re-introduced for 3.3.0 + * + * Transaction invariants are disabled due to a performance regression: + * the two-pass design (transaction-specific invariants + protocol invariants) + * iterates over modified ledger entries twice per transaction. + * + * Until resolved, only protocol invariants are checked (delegated to ctx_). + * This is safe because all transaction invariants in 3.2.0 are no-ops. + */ + return ctx_.checkInvariants(result, fee); } //------------------------------------------------------------------------------ ApplyResult From dd0b6754d4c33d906d54f317dcc9f4da800488ec Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 10 Jun 2026 15:45:51 +0100 Subject: [PATCH 82/87] ci: Add `gh` and `file` to nix packages (#7444) --- cspell.config.yaml | 1 + flake.lock | 6 +++--- nix/docker/check-tools.sh | 2 ++ nix/packages.nix | 2 ++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cspell.config.yaml b/cspell.config.yaml index cab2fc3da6..926ac06596 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -84,6 +84,7 @@ words: - coro - coros - cowid + - cpack - cryptocondition - cryptoconditional - cryptoconditions diff --git a/flake.lock b/flake.lock index 2013cfabd4..f8553af703 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1780243769, - "narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=", + "lastModified": 1780749050, + "narHash": "sha256-3av0pIjlOWQ6rDbNOmpUSvbNnJkGORQKKjb4LtCZsIY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "331800de5053fcebacf6813adb5db9c9dca22a0c", + "rev": "a799d3e3886da994fa307f817a6bc705ae538eeb", "type": "github" }, "original": { diff --git a/nix/docker/check-tools.sh b/nix/docker/check-tools.sh index 67bcdff8a9..276e5977ff 100755 --- a/nix/docker/check-tools.sh +++ b/nix/docker/check-tools.sh @@ -10,10 +10,12 @@ cmake --version conan --version curl --version doxygen --version +file --version g++ --version gcc --version gcov --version gcovr --version +gh --version git --version git-cliff --version gpg --version diff --git a/nix/packages.nix b/nix/packages.nix index d40472634b..fc4eff679e 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -13,7 +13,9 @@ in conan curlMinimal # needed for codecov/codecov-action doxygen + file # needed for cpack in Clio gcovr + gh git git-cliff gnumake From 1f359f719cbf475e6c010ad0fcf5b4044c6768d1 Mon Sep 17 00:00:00 2001 From: Shi Cheng <97218929+shichengripple001@users.noreply.github.com> Date: Thu, 11 Jun 2026 01:24:44 +0800 Subject: [PATCH 83/87] fix: Add [[maybe_unused]] to fix320Enabled for assert=OFF builds (#7446) Co-authored-by: Claude Sonnet 4.6 --- src/libxrpl/ledger/helpers/LendingHelpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/ledger/helpers/LendingHelpers.cpp b/src/libxrpl/ledger/helpers/LendingHelpers.cpp index 676b473132..f7ec8a8bc3 100644 --- a/src/libxrpl/ledger/helpers/LendingHelpers.cpp +++ b/src/libxrpl/ledger/helpers/LendingHelpers.cpp @@ -813,7 +813,7 @@ doOverpayment( // 3. The overpayment's penalty interest part (= untrackedInterest // for the overpayment path; see computeOverpaymentComponents): // trackedInterestPart() - bool const fix320Enabled = rules.enabled(fixCleanup3_2_0); + [[maybe_unused]] bool const fix320Enabled = rules.enabled(fixCleanup3_2_0); XRPL_ASSERT_IF( fix320Enabled, overpaymentComponents.trackedPrincipalDelta == From 8000adfa797f3d5c81284cb3bcc8c6e9213549ec Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 10 Jun 2026 19:08:34 +0100 Subject: [PATCH 84/87] ci: Make configurations launch on certain event types (#7447) --- .github/scripts/strategy-matrix/generate.py | 57 ++++++++++++++++--- .github/scripts/strategy-matrix/linux.json | 3 +- .github/scripts/strategy-matrix/macos.json | 3 +- .github/scripts/strategy-matrix/windows.json | 6 +- .../workflows/reusable-strategy-matrix.yml | 3 +- 5 files changed, 61 insertions(+), 11 deletions(-) diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index aaf84a51d0..6353567f27 100755 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -27,6 +27,19 @@ def get_cmake_args(build_type: str, extra_args: str) -> str: return " ".join(args) +def runs_on_event(exclude_event_types: list[str], event: str | None) -> bool: + """Whether a config should run for the current event. + + 'exclude_event_types' is a list of GitHub event names (e.g. + ["pull_request"]) on which the config should NOT run; an empty list means + the config runs on every event. When no event is given (event is None), no + filtering is applied. + """ + if event is None: + return True + return event not in exclude_event_types + + # --------------------------------------------------------------------------- # Input types — shapes of the JSON config files # --------------------------------------------------------------------------- @@ -43,6 +56,9 @@ class LinuxConfig: suffix: str = "" extra_cmake_args: str = "" image: str = "" # only used by package_configs entries + # List of GitHub event names (e.g. "pull_request") on which this config + # should NOT run. Empty means it runs on every event. + exclude_event_types: list[str] = dataclasses.field(default_factory=list) @dataclasses.dataclass @@ -77,6 +93,9 @@ class PlatformConfig: build_type: list[str] build_only: bool = False # if true, skip tests (e.g. macos/Windows Debug) extra_cmake_args: str = "" + # List of GitHub event names (e.g. "pull_request") on which this config + # should NOT run. Empty means it runs on every event. + exclude_event_types: list[str] = dataclasses.field(default_factory=list) def __post_init__(self) -> None: if isinstance(self.build_type, str): @@ -151,16 +170,21 @@ _ARCHS: dict[str, Architecture] = { } -def expand_linux_matrix(linux: LinuxFile) -> list[MatrixEntry]: +def expand_linux_matrix( + linux: LinuxFile, event: str | None = None +) -> list[MatrixEntry]: """Expand a LinuxFile into a flat list of matrix entries. Each config entry is expanded over the cross-product of its - compiler, build_type, sanitizers, and architecture lists. + compiler, build_type, sanitizers, and architecture lists. Configs that + exclude the current event are skipped. """ entries: list[MatrixEntry] = [] for distro, configs in linux.configs.items(): for cfg in configs: + if not runs_on_event(cfg.exclude_event_types, event): + continue # An empty sanitizers list means "one entry with no sanitizer". effective_sanitizers = cfg.sanitizers or [""] effective_archs = {arch: _ARCHS[arch] for arch in cfg.arch} @@ -218,13 +242,20 @@ def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]: return entries -def expand_platform_matrix(pf: PlatformFile) -> list[MatrixEntry]: - """Expand a PlatformFile (macOS or Windows) into matrix entries.""" +def expand_platform_matrix( + pf: PlatformFile, event: str | None = None +) -> list[MatrixEntry]: + """Expand a PlatformFile (macOS or Windows) into matrix entries. + + Configs that exclude the current event are skipped. + """ platform_name, arch = pf.platform.split("/") is_windows = platform_name == "windows" entries: list[MatrixEntry] = [] for cfg in pf.configs: + if not runs_on_event(cfg.exclude_event_types, event): + continue for build_type in cfg.build_type: entries.append( MatrixEntry( @@ -262,6 +293,14 @@ if __name__ == "__main__": help="Emit the Linux packaging matrix instead of the build/test matrix.", action="store_true", ) + parser.add_argument( + "-e", + "--event", + help="The GitHub event name that triggered the workflow (e.g. 'push', " + "'pull_request'). Configs are filtered by their 'event_type'. If " + "omitted, no filtering is applied.", + default=None, + ) args = parser.parse_args() matrix: list[MatrixEntry] | list[PackagingEntry] = [] @@ -270,12 +309,16 @@ if __name__ == "__main__": matrix = expand_linux_packaging(LinuxFile.load(THIS_DIR / "linux.json")) else: if args.config in ("linux", None): - matrix += expand_linux_matrix(LinuxFile.load(THIS_DIR / "linux.json")) + matrix += expand_linux_matrix( + LinuxFile.load(THIS_DIR / "linux.json"), args.event + ) if args.config in ("macos", None): - matrix += expand_platform_matrix(PlatformFile.load(THIS_DIR / "macos.json")) + matrix += expand_platform_matrix( + PlatformFile.load(THIS_DIR / "macos.json"), args.event + ) if args.config in ("windows", None): matrix += expand_platform_matrix( - PlatformFile.load(THIS_DIR / "windows.json") + PlatformFile.load(THIS_DIR / "windows.json"), args.event ) print(f"matrix={json.dumps({'include': [dataclasses.asdict(e) for e in matrix]})}") diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index edacdbde4c..e6c807ac95 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -41,7 +41,8 @@ "build_type": ["Debug"], "arch": ["amd64"], "suffix": "unity", - "extra_cmake_args": "-Dunity=ON" + "extra_cmake_args": "-Dunity=ON", + "exclude_event_types": ["pull_request"] } ], diff --git a/.github/scripts/strategy-matrix/macos.json b/.github/scripts/strategy-matrix/macos.json index 5b9e32f88e..66d7a55a43 100644 --- a/.github/scripts/strategy-matrix/macos.json +++ b/.github/scripts/strategy-matrix/macos.json @@ -9,7 +9,8 @@ { "build_type": "Debug", "extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5", - "build_only": true + "build_only": true, + "exclude_event_types": ["pull_request"] } ] } diff --git a/.github/scripts/strategy-matrix/windows.json b/.github/scripts/strategy-matrix/windows.json index e4678b60db..e25f9ad131 100644 --- a/.github/scripts/strategy-matrix/windows.json +++ b/.github/scripts/strategy-matrix/windows.json @@ -3,6 +3,10 @@ "runner": ["self-hosted", "Windows", "devbox"], "configs": [ { "build_type": "Release" }, - { "build_type": "Debug", "build_only": true } + { + "build_type": "Debug", + "build_only": true, + "exclude_event_types": ["pull_request"] + } ] } diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index ea134b43b2..4518a8ffef 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -35,4 +35,5 @@ jobs: id: generate env: GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}', inputs.os) || '' }} - run: ./generate.py ${GENERATE_CONFIG} >>"${GITHUB_OUTPUT}" + GENERATE_EVENT: ${{ github.event_name }} + run: ./generate.py ${GENERATE_CONFIG} --event="${GENERATE_EVENT}" >>"${GITHUB_OUTPUT}" From 2f6b466feb716198bf62dacd55b23332dd09e16c Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 10 Jun 2026 19:24:34 +0100 Subject: [PATCH 85/87] ci: Make sanitizer flags lists in the profile, not a string (#7449) --- conan/profiles/sanitizers | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/conan/profiles/sanitizers b/conan/profiles/sanitizers index 4a05fda734..083807ea9e 100644 --- a/conan/profiles/sanitizers +++ b/conan/profiles/sanitizers @@ -52,52 +52,50 @@ include(default) {% endif %} {# Frame pointer required for meaningful stack traces; -O1 for reasonable performance #} -{% set compile_flags = ["-fno-omit-frame-pointer", "-O1"] %} +{% set sanitizer_compiler_flags = ["-fno-omit-frame-pointer", "-O1"] %} {% if compiler == "gcc" %} {# Suppress false positive warnings with GCC #} - {% set _ = compile_flags.append("-Wno-stringop-overflow") %} + {% set _ = sanitizer_compiler_flags.append("-Wno-stringop-overflow") %} {% set relocation_flags = [] %} {% if arch == "x86_64" and enable_asan %} {# Large code model prevents relocation errors in instrumented ASAN binaries #} - {% set _ = compile_flags.append("-mcmodel=large") %} + {% set _ = sanitizer_compiler_flags.append("-mcmodel=large") %} {% set _ = relocation_flags.append("-mcmodel=large") %} {% elif enable_tsan %} {# GCC doesn't support atomic_thread_fence with TSAN; suppress warnings #} - {% set _ = compile_flags.append("-Wno-tsan") %} + {% set _ = sanitizer_compiler_flags.append("-Wno-tsan") %} {% if arch == "x86_64" %} {# Medium code model for TSAN; large is incompatible #} - {% set _ = compile_flags.append("-mcmodel=medium") %} + {% set _ = sanitizer_compiler_flags.append("-mcmodel=medium") %} {% set _ = relocation_flags.append("-mcmodel=medium") %} {% endif %} {% endif %} {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} - {% set _ = compile_flags.append(fsanitize) %} + {% set _ = sanitizer_compiler_flags.append(fsanitize) %} {% set _ = relocation_flags.append(fsanitize) %} - {% set sanitizer_compiler_flags = " ".join(compile_flags) %} - {% set sanitizer_linker_flags = " ".join(relocation_flags) %} + {% set sanitizer_linker_flags = relocation_flags %} {% elif compiler == "clang" or compiler == "apple-clang" %} {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} - {% set _ = compile_flags.append(fsanitize) %} + {% set _ = sanitizer_compiler_flags.append(fsanitize) %} - {% set sanitizer_compiler_flags = " ".join(compile_flags) %} - {% set sanitizer_linker_flags = fsanitize %} + {% set sanitizer_linker_flags = [fsanitize] %} {% endif %} [conf] tools.build:defines+={{defines}} -tools.build:cxxflags+=['{{sanitizer_compiler_flags}}'] -tools.build:sharedlinkflags+=['{{sanitizer_linker_flags}}'] -tools.build:exelinkflags+=['{{sanitizer_linker_flags}}'] +tools.build:cxxflags+={{sanitizer_compiler_flags}} +tools.build:sharedlinkflags+={{sanitizer_linker_flags}} +tools.build:exelinkflags+={{sanitizer_linker_flags}} tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"] # &: means "apply only to the consumer/root package" -&:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{sanitizers}}", "SANITIZERS_COMPILER_FLAGS": "{{sanitizer_compiler_flags}}", "SANITIZERS_LINKER_FLAGS": "{{sanitizer_linker_flags}}"} +&:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{sanitizers}}", "SANITIZERS_COMPILER_FLAGS": "{{sanitizer_compiler_flags | join(' ')}}", "SANITIZERS_LINKER_FLAGS": "{{sanitizer_linker_flags | join(' ')}}"} [options] {% if enable_asan %} From 09c36d066ec6e1ffdcc6b98075c991e202873d5f Mon Sep 17 00:00:00 2001 From: Zhiyuan Wang <96991820+Kassaking7@users.noreply.github.com> Date: Wed, 10 Jun 2026 16:42:41 -0400 Subject: [PATCH 86/87] fix: Correct hybrid offer deletion on credential expiry (#6843) Co-authored-by: Bart --- include/xrpl/protocol/detail/features.macro | 1 + src/libxrpl/tx/paths/OfferStream.cpp | 9 +- src/test/app/PermissionedDEX_test.cpp | 801 ++++++++++++-------- 3 files changed, 475 insertions(+), 336 deletions(-) diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 2b2f24ba53..d3500ab144 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_FIX (Cleanup3_3_0, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FIX (Cleanup3_2_0, Supported::Yes, VoteBehavior::DefaultNo) XRPL_FEATURE(MPTokensV2, Supported::No, VoteBehavior::DefaultNo) XRPL_FIX (Cleanup3_1_3, Supported::Yes, VoteBehavior::DefaultYes) diff --git a/src/libxrpl/tx/paths/OfferStream.cpp b/src/libxrpl/tx/paths/OfferStream.cpp index b7defb4df8..ecc8416a2b 100644 --- a/src/libxrpl/tx/paths/OfferStream.cpp +++ b/src/libxrpl/tx/paths/OfferStream.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -249,7 +250,13 @@ TOfferStreamBase::step() continue; } - if (entry->isFieldPresent(sfDomainID) && + // Pre-fixCleanup3_3_0: validate domain membership for any book. + // Post-fixCleanup3_3_0: only validate when walking a domain book. + // Hybrid offers carry sfDomainID but also participate in the open + // book; expiry of the owner's domain credential should not evict + // the offer from the open book. + if ((!view_.rules().enabled(fixCleanup3_3_0) || book_.domain.has_value()) && + entry->isFieldPresent(sfDomainID) && !permissioned_dex::offerInDomain( view_, entry->key(), entry->getFieldH256(sfDomainID), j_)) { diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index d534f20248..99e69ce482 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -185,15 +185,15 @@ class PermissionedDEX_test : public beast::unit_test::Suite // test preflight { Env env(*this, features - featurePermissionedDEX); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - env(offer(bob_, XRP(10), USD(10)), Domain(domainID), Ter(temDISABLED)); + env(offer(bob, XRP(10), USD(10)), Domain(domainID), Ter(temDISABLED)); env.close(); env.enableFeature(featurePermissionedDEX); env.close(); - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); } @@ -214,7 +214,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite // preclaim - someone outside of the domain cannot create domain offer { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); // create devin account who is not part of the domain @@ -223,7 +223,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); env.trust(USD(1000), devin); env.close(); - env(pay(gw_, devin, USD(100))); + env(pay(gw, devin, USD(100))); env.close(); env(offer(devin, XRP(10), USD(10)), Domain(domainID), Ter(tecNO_PERMISSION)); @@ -247,7 +247,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite // preclaim - someone with expired cred cannot create domain offer { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); // create devin account who is not part of the domain @@ -256,7 +256,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); env.trust(USD(1000), devin); env.close(); - env(pay(gw_, devin, USD(100))); + env(pay(gw, devin, USD(100))); env.close(); auto jv = credentials::create(devin, domainOwner, credType); @@ -282,13 +282,13 @@ class PermissionedDEX_test : public beast::unit_test::Suite // preclaim - cannot create an offer in a non existent domain { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); uint256 const badDomain{ "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134" "E5"}; - env(offer(bob_, XRP(10), USD(10)), Domain(badDomain), Ter(tecNO_PERMISSION)); + env(offer(bob, XRP(10), USD(10)), Domain(badDomain), Ter(tecNO_PERMISSION)); env.close(); } @@ -296,68 +296,68 @@ class PermissionedDEX_test : public beast::unit_test::Suite // domain { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - env(credentials::deleteCred(domainOwner, gw_, domainOwner, credType)); + env(credentials::deleteCred(domainOwner, gw, domainOwner, credType)); env.close(); - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + auto const bobOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true)); } // apply - offer can be created even if takerpays issuer is not in // domain { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - env(credentials::deleteCred(domainOwner, gw_, domainOwner, credType)); + env(credentials::deleteCred(domainOwner, gw, domainOwner, credType)); env.close(); - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(bob_, USD(10), XRP(10)), Domain(domainID)); + auto const bobOfferSeq{env.seq(bob)}; + env(offer(bob, USD(10), XRP(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, USD(10), XRP(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, USD(10), XRP(10), 0, true)); } // apply - two domain offers cross with each other { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + auto const bobOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), 0, true)); - BEAST_EXPECT(ownerCount(env, bob_) == 3); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(ownerCount(env, bob) == 3); // a non domain offer cannot cross with domain offer - env(offer(carol_, USD(10), XRP(10))); + env(offer(carol, USD(10), XRP(10))); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true)); - auto const aliceOfferSeq{env.seq(alice_)}; - env(offer(alice_, USD(10), XRP(10)), Domain(domainID)); + auto const aliceOfferSeq{env.seq(alice)}; + env(offer(alice, USD(10), XRP(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(!offerExists(env, alice_, aliceOfferSeq)); - BEAST_EXPECT(!offerExists(env, bob_, bobOfferSeq)); - BEAST_EXPECT(ownerCount(env, alice_) == 2); + BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq)); + BEAST_EXPECT(ownerCount(env, alice) == 2); } // apply - create lots of domain offers { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); std::vector offerSeqs; @@ -365,19 +365,19 @@ class PermissionedDEX_test : public beast::unit_test::Suite for (size_t i = 0; i <= 100; i++) { - auto const bobOfferSeq{env.seq(bob_)}; + auto const bobOfferSeq{env.seq(bob)}; offerSeqs.emplace_back(bobOfferSeq); - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true)); } for (auto const offerSeq : offerSeqs) { - env(offerCancel(bob_, offerSeq)); + env(offerCancel(bob, offerSeq)); env.close(); - BEAST_EXPECT(!offerExists(env, bob_, offerSeq)); + BEAST_EXPECT(!offerExists(env, bob, offerSeq)); } } } @@ -390,10 +390,10 @@ class PermissionedDEX_test : public beast::unit_test::Suite // test preflight - without enabling featurePermissionedDEX amendment { Env env(*this, features - featurePermissionedDEX); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - env(pay(bob_, alice_, USD(10)), + env(pay(bob, alice, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID), @@ -403,10 +403,10 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.enableFeature(featurePermissionedDEX); env.close(); - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - env(pay(bob_, alice_, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); + env(pay(bob, alice, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); env.close(); } @@ -431,13 +431,13 @@ class PermissionedDEX_test : public beast::unit_test::Suite // preclaim - cannot send payment with non existent domain { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); uint256 const badDomain{ "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134" "E5"}; - env(pay(bob_, alice_, USD(10)), + env(pay(bob, alice, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(badDomain), @@ -448,10 +448,10 @@ class PermissionedDEX_test : public beast::unit_test::Suite // preclaim - payment with non-domain destination fails { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); // create devin account who is not part of the domain @@ -460,11 +460,11 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); env.trust(USD(1000), devin); env.close(); - env(pay(gw_, devin, USD(100))); + env(pay(gw, devin, USD(100))); env.close(); // devin is not part of domain - env(pay(alice_, devin, USD(10)), + env(pay(alice, devin, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID), @@ -476,7 +476,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); // devin has not yet accepted cred - env(pay(alice_, devin, USD(10)), + env(pay(alice, devin, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID), @@ -487,17 +487,17 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); // devin can now receive payment after he is in domain - env(pay(alice_, devin, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); + env(pay(alice, devin, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); env.close(); } // preclaim - non-domain sender cannot send payment { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); // create devin account who is not part of the domain @@ -506,11 +506,11 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); env.trust(USD(1000), devin); env.close(); - env(pay(gw_, devin, USD(100))); + env(pay(gw, devin, USD(100))); env.close(); // devin tries to send domain payment - env(pay(devin, alice_, USD(10)), + env(pay(devin, alice, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID), @@ -522,7 +522,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); // devin has not yet accepted cred - env(pay(devin, alice_, USD(10)), + env(pay(devin, alice, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID), @@ -533,28 +533,28 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); // devin can now send payment after he is in domain - env(pay(devin, alice_, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); + env(pay(devin, alice, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); env.close(); } // apply - domain owner can always send and receive domain payment { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); // domain owner can always be destination - env(pay(alice_, domainOwner, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); + env(pay(alice, domainOwner, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); env.close(); - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); // domain owner can send - env(pay(domainOwner, alice_, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); + env(pay(domainOwner, alice, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); env.close(); } } @@ -567,22 +567,22 @@ class PermissionedDEX_test : public beast::unit_test::Suite // test domain cross currency payment consuming one offer { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); // create a regular offer without domain - auto const regularOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10))); + auto const regularOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10))); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, regularOfferSeq, XRP(10), USD(10))); + BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10))); - auto const regularDirKey = getDefaultOfferDirKey(env, bob_, regularOfferSeq); + auto const regularDirKey = getDefaultOfferDirKey(env, bob, regularOfferSeq); BEAST_EXPECT(regularDirKey); BEAST_EXPECT(checkDirectorySize( env, *regularDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) // a domain payment cannot consume regular offers - env(pay(alice_, carol_, USD(10)), + env(pay(alice, carol, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID), @@ -590,23 +590,23 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); // create a domain offer - auto const domainOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + auto const domainOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, domainOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, domainOfferSeq, XRP(10), USD(10), 0, true)); - auto const domainDirKey = getDefaultOfferDirKey(env, bob_, domainOfferSeq); + auto const domainDirKey = getDefaultOfferDirKey(env, bob, domainOfferSeq); BEAST_EXPECT(domainDirKey); BEAST_EXPECT(checkDirectorySize( env, *domainDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) // cross-currency permissioned payment consumed // domain offer instead of regular offer - env(pay(alice_, carol_, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); + env(pay(alice, carol, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(!offerExists(env, bob_, domainOfferSeq)); - BEAST_EXPECT(checkOffer(env, bob_, regularOfferSeq, XRP(10), USD(10))); + BEAST_EXPECT(!offerExists(env, bob, domainOfferSeq)); + BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10))); // domain directory is empty BEAST_EXPECT(checkDirectorySize( @@ -618,79 +618,79 @@ class PermissionedDEX_test : public beast::unit_test::Suite // test domain payment consuming two offers in the path { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const eur = gw_["EUR"]; - env.trust(eur(1000), alice_); + auto const eur = gw["EUR"]; + env.trust(eur(1000), alice); env.close(); - env.trust(eur(1000), bob_); + env.trust(eur(1000), bob); env.close(); - env.trust(eur(1000), carol_); + env.trust(eur(1000), carol); env.close(); - env(pay(gw_, bob_, eur(100))); + env(pay(gw, bob, eur(100))); env.close(); // create XRP/USD domain offer - auto const usdOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + auto const usdOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, usdOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true)); // payment fail because there isn't eur offer - env(pay(alice_, carol_, eur(10)), + env(pay(alice, carol, eur(10)), Path(~USD, ~eur), Sendmax(XRP(10)), Domain(domainID), Ter(tecPATH_PARTIAL)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, usdOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true)); - // bob_ creates a regular USD/EUR offer - auto const regularOfferSeq{env.seq(bob_)}; - env(offer(bob_, USD(10), eur(10))); + // bob creates a regular USD/EUR offer + auto const regularOfferSeq{env.seq(bob)}; + env(offer(bob, USD(10), eur(10))); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, regularOfferSeq, USD(10), eur(10))); + BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, USD(10), eur(10))); - // alice_ tries to pay again, but still fails because the regular + // alice tries to pay again, but still fails because the regular // offer cannot be consumed - env(pay(alice_, carol_, eur(10)), + env(pay(alice, carol, eur(10)), Path(~USD, ~eur), Sendmax(XRP(10)), Domain(domainID), Ter(tecPATH_PARTIAL)); env.close(); - // bob_ creates a domain USD/EUR offer - auto const eurOfferSeq{env.seq(bob_)}; - env(offer(bob_, USD(10), eur(10)), Domain(domainID)); + // bob creates a domain USD/EUR offer + auto const eurOfferSeq{env.seq(bob)}; + env(offer(bob, USD(10), eur(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, eurOfferSeq, USD(10), eur(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, eurOfferSeq, USD(10), eur(10), 0, true)); - // alice_ successfully consume two domain offers: xrp/usd and usd/eur - env(pay(alice_, carol_, eur(5)), Sendmax(XRP(5)), Domain(domainID), Path(~USD, ~eur)); + // alice successfully consume two domain offers: xrp/usd and usd/eur + env(pay(alice, carol, eur(5)), Sendmax(XRP(5)), Domain(domainID), Path(~USD, ~eur)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, usdOfferSeq, XRP(5), USD(5), 0, true)); - BEAST_EXPECT(checkOffer(env, bob_, eurOfferSeq, USD(5), eur(5), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(5), USD(5), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, eurOfferSeq, USD(5), eur(5), 0, true)); - // alice_ successfully consume two domain offers and deletes them + // alice successfully consume two domain offers and deletes them // we compute path this time using `paths` - env(pay(alice_, carol_, eur(5)), Sendmax(XRP(5)), Domain(domainID), Paths(XRP)); + env(pay(alice, carol, eur(5)), Sendmax(XRP(5)), Domain(domainID), Paths(XRP)); env.close(); - BEAST_EXPECT(!offerExists(env, bob_, usdOfferSeq)); - BEAST_EXPECT(!offerExists(env, bob_, eurOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, usdOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, eurOfferSeq)); // regular offer is not consumed - BEAST_EXPECT(checkOffer(env, bob_, regularOfferSeq, USD(10), eur(10))); + BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, USD(10), eur(10))); } // domain payment cannot consume offer from another domain { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); // Fund devin and create USD trustline @@ -700,7 +700,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); env.trust(USD(1000), devin); env.close(); - env(pay(gw_, devin, USD(100))); + env(pay(gw, devin, USD(100))); env.close(); auto const badCredType = "badCred"; @@ -720,24 +720,24 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); // domain payment can't consume an offer from another domain - env(pay(alice_, carol_, USD(10)), + env(pay(alice, carol, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID), Ter(tecPATH_PARTIAL)); env.close(); - // bob_ creates an offer under the right domain - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + // bob creates an offer under the right domain + auto const bobOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true)); // domain payment now consumes from the right domain - env(pay(alice_, carol_, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); + env(pay(alice, carol, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(!offerExists(env, bob_, bobOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq)); } // sanity check: devin, who is part of the domain but doesn't have a @@ -745,10 +745,10 @@ class PermissionedDEX_test : public beast::unit_test::Suite // offer { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); // fund devin but don't create a USD trustline with gateway @@ -764,14 +764,14 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); // successful payment because offer is consumed - env(pay(devin, alice_, USD(10)), Sendmax(XRP(10)), Domain(domainID)); + env(pay(devin, alice, USD(10)), Sendmax(XRP(10)), Domain(domainID)); env.close(); } // offer becomes unfunded when offer owner's cred expires { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); // create devin account who is not part of the domain @@ -780,7 +780,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); env.trust(USD(1000), devin); env.close(); - env(pay(gw_, devin, USD(100))); + env(pay(gw, devin, USD(100))); env.close(); auto jv = credentials::create(devin, domainOwner, credType); @@ -797,7 +797,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); // devin's offer can still be consumed while his cred isn't expired - env(pay(alice_, carol_, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID)); + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID)); env.close(); BEAST_EXPECT(checkOffer(env, devin, offerSeq, XRP(5), USD(5), 0, true)); @@ -805,7 +805,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(std::chrono::seconds(20)); // devin's offer is unfunded now due to expired cred - env(pay(alice_, carol_, USD(5)), + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID), @@ -817,30 +817,30 @@ class PermissionedDEX_test : public beast::unit_test::Suite // offer becomes unfunded when offer owner's cred is removed { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const offerSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + auto const offerSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - // bob_'s offer can still be consumed while his cred exists - env(pay(alice_, carol_, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID)); + // bob's offer can still be consumed while his cred exists + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, offerSeq, XRP(5), USD(5), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, offerSeq, XRP(5), USD(5), 0, true)); - // remove bob_'s cred - env(credentials::deleteCred(domainOwner, bob_, domainOwner, credType)); + // remove bob's cred + env(credentials::deleteCred(domainOwner, bob, domainOwner, credType)); env.close(); - // bob_'s offer is unfunded now due to expired cred - env(pay(alice_, carol_, USD(5)), + // bob's offer is unfunded now due to expired cred + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID), Ter(tecPATH_PARTIAL)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, offerSeq, XRP(5), USD(5), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, offerSeq, XRP(5), USD(5), 0, true)); } } @@ -853,34 +853,34 @@ class PermissionedDEX_test : public beast::unit_test::Suite // payment. If the domain wishes to control who is allowed to ripple // through, they should set the rippling individually Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const eura = alice_["EUR"]; - auto const eurb = bob_["EUR"]; + auto const eura = alice["EUR"]; + auto const eurb = bob["EUR"]; - env.trust(eura(100), bob_); - env.trust(eurb(100), carol_); + env.trust(eura(100), bob); + env.trust(eurb(100), carol); env.close(); - // remove bob_ from domain - env(credentials::deleteCred(domainOwner, bob_, domainOwner, credType)); + // remove bob from domain + env(credentials::deleteCred(domainOwner, bob, domainOwner, credType)); env.close(); - // alice_ can still ripple through bob_ even though he's not part + // alice can still ripple through bob even though he's not part // of the domain, this is intentional - env(pay(alice_, carol_, eurb(10)), Paths(eura), Domain(domainID)); + env(pay(alice, carol, eurb(10)), Paths(eura), Domain(domainID)); env.close(); - env.require(Balance(bob_, eura(10)), Balance(carol_, eurb(10))); + env.require(Balance(bob, eura(10)), Balance(carol, eurb(10))); - // carol_ sets no ripple on bob_ - env(trust(carol_, bob_["EUR"](0), bob_, tfSetNoRipple)); + // carol sets no ripple on bob + env(trust(carol, bob["EUR"](0), bob, tfSetNoRipple)); env.close(); - // payment no longer works because carol_ has no ripple on bob_ - env(pay(alice_, carol_, eurb(5)), Paths(eura), Domain(domainID), Ter(tecPATH_DRY)); + // payment no longer works because carol has no ripple on bob + env(pay(alice, carol, eurb(5)), Paths(eura), Domain(domainID), Ter(tecPATH_DRY)); env.close(); - env.require(Balance(bob_, eura(10)), Balance(carol_, eurb(10))); + env.require(Balance(bob, eura(10)), Balance(carol, eurb(10))); } void @@ -891,37 +891,37 @@ class PermissionedDEX_test : public beast::unit_test::Suite // whether the issuer is in the domain should NOT affect whether an // offer can be consumed in domain payment Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); // create an xrp/usd offer with usd as takergets - auto const bobOffer1Seq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + auto const bobOffer1Seq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); // create an usd/xrp offer with usd as takerpays - auto const bobOffer2Seq{env.seq(bob_)}; - env(offer(bob_, USD(10), XRP(10)), Domain(domainID), Txflags(tfPassive)); + auto const bobOffer2Seq{env.seq(bob)}; + env(offer(bob, USD(10), XRP(10)), Domain(domainID), Txflags(tfPassive)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOffer1Seq, XRP(10), USD(10), 0, true)); - BEAST_EXPECT(checkOffer(env, bob_, bobOffer2Seq, USD(10), XRP(10), lsfPassive, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOffer1Seq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOffer2Seq, USD(10), XRP(10), lsfPassive, true)); // remove gateway from domain - env(credentials::deleteCred(domainOwner, gw_, domainOwner, credType)); + env(credentials::deleteCred(domainOwner, gw, domainOwner, credType)); env.close(); // payment succeeds even if issuer is not in domain // xrp/usd offer is consumed - env(pay(alice_, carol_, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); + env(pay(alice, carol, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(!offerExists(env, bob_, bobOffer1Seq)); + BEAST_EXPECT(!offerExists(env, bob, bobOffer1Seq)); // payment succeeds even if issuer is not in domain // usd/xrp offer is consumed - env(pay(alice_, carol_, XRP(10)), Path(~XRP), Sendmax(USD(10)), Domain(domainID)); + env(pay(alice, carol, XRP(10)), Path(~XRP), Sendmax(USD(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(!offerExists(env, bob_, bobOffer2Seq)); + BEAST_EXPECT(!offerExists(env, bob, bobOffer2Seq)); } void @@ -932,36 +932,36 @@ class PermissionedDEX_test : public beast::unit_test::Suite // checking that an unfunded offer will be implicitly removed by a // successful payment tx Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const aliceOfferSeq{env.seq(alice_)}; - env(offer(alice_, XRP(100), USD(100)), Domain(domainID)); + auto const aliceOfferSeq{env.seq(alice)}; + env(offer(alice, XRP(100), USD(100)), Domain(domainID)); env.close(); - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(20), USD(20)), Domain(domainID)); + auto const bobOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(20), USD(20)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(20), USD(20), 0, true)); - BEAST_EXPECT(checkOffer(env, alice_, aliceOfferSeq, XRP(100), USD(100), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(20), USD(20), 0, true)); + BEAST_EXPECT(checkOffer(env, alice, aliceOfferSeq, XRP(100), USD(100), 0, true)); - auto const domainDirKey = getDefaultOfferDirKey(env, bob_, bobOfferSeq); + auto const domainDirKey = getDefaultOfferDirKey(env, bob, bobOfferSeq); BEAST_EXPECT(domainDirKey); BEAST_EXPECT(checkDirectorySize( env, *domainDirKey, 2)); // NOLINT(bugprone-unchecked-optional-access) - // remove alice_ from domain and thus alice_'s offer becomes unfunded - env(credentials::deleteCred(domainOwner, alice_, domainOwner, credType)); + // remove alice from domain and thus alice's offer becomes unfunded + env(credentials::deleteCred(domainOwner, alice, domainOwner, credType)); env.close(); - env(pay(gw_, carol_, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); + env(pay(gw, carol, USD(10)), Path(~USD), Sendmax(XRP(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true)); - // alice_'s unfunded offer is removed implicitly - BEAST_EXPECT(!offerExists(env, alice_, aliceOfferSeq)); + // alice's unfunded offer is removed implicitly + BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq)); BEAST_EXPECT(checkDirectorySize( env, *domainDirKey, 1)); // NOLINT(bugprone-unchecked-optional-access) } @@ -972,12 +972,12 @@ class PermissionedDEX_test : public beast::unit_test::Suite testcase("AMM not used"); Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - AMM const amm(env, alice_, XRP(10), USD(50)); + AMM const amm(env, alice, XRP(10), USD(50)); // a domain payment isn't able to consume AMM - env(pay(bob_, carol_, USD(5)), + env(pay(bob, carol, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID), @@ -985,7 +985,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite env.close(); // a non domain payment can use AMM - env(pay(bob_, carol_, USD(5)), Path(~USD), Sendmax(XRP(5))); + env(pay(bob, carol, USD(5)), Path(~USD), Sendmax(XRP(5))); env.close(); // USD amount in AMM is changed @@ -1001,126 +1001,126 @@ class PermissionedDEX_test : public beast::unit_test::Suite // test preflight - invalid hybrid flag { Env env(*this, features - featurePermissionedDEX); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - env(offer(bob_, XRP(10), USD(10)), + env(offer(bob, XRP(10), USD(10)), Domain(domainID), Txflags(tfHybrid), Ter(temDISABLED)); env.close(); - env(offer(bob_, XRP(10), USD(10)), Txflags(tfHybrid), Ter(temINVALID_FLAG)); + env(offer(bob, XRP(10), USD(10)), Txflags(tfHybrid), Ter(temINVALID_FLAG)); env.close(); env.enableFeature(featurePermissionedDEX); env.close(); // hybrid offer must have domainID - env(offer(bob_, XRP(10), USD(10)), Txflags(tfHybrid), Ter(temINVALID_FLAG)); + env(offer(bob, XRP(10), USD(10)), Txflags(tfHybrid), Ter(temINVALID_FLAG)); env.close(); // hybrid offer must have domainID - auto const offerSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); + auto const offerSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, offerSeq, XRP(10), USD(10), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, offerSeq, XRP(10), USD(10), lsfHybrid, true)); } // apply - domain offer can cross with hybrid { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); + auto const bobOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true)); - BEAST_EXPECT(offerExists(env, bob_, bobOfferSeq)); - BEAST_EXPECT(ownerCount(env, bob_) == 3); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true)); + BEAST_EXPECT(offerExists(env, bob, bobOfferSeq)); + BEAST_EXPECT(ownerCount(env, bob) == 3); - auto const aliceOfferSeq{env.seq(alice_)}; - env(offer(alice_, USD(10), XRP(10)), Domain(domainID)); + auto const aliceOfferSeq{env.seq(alice)}; + env(offer(alice, USD(10), XRP(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(!offerExists(env, alice_, aliceOfferSeq)); - BEAST_EXPECT(!offerExists(env, bob_, bobOfferSeq)); - BEAST_EXPECT(ownerCount(env, alice_) == 2); + BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq)); + BEAST_EXPECT(ownerCount(env, alice) == 2); } // apply - open offer can cross with hybrid { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); + auto const bobOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); env.close(); - BEAST_EXPECT(offerExists(env, bob_, bobOfferSeq)); - BEAST_EXPECT(ownerCount(env, bob_) == 3); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true)); + BEAST_EXPECT(offerExists(env, bob, bobOfferSeq)); + BEAST_EXPECT(ownerCount(env, bob) == 3); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true)); - auto const aliceOfferSeq{env.seq(alice_)}; - env(offer(alice_, USD(10), XRP(10))); + auto const aliceOfferSeq{env.seq(alice)}; + env(offer(alice, USD(10), XRP(10))); env.close(); - BEAST_EXPECT(!offerExists(env, alice_, aliceOfferSeq)); - BEAST_EXPECT(!offerExists(env, bob_, bobOfferSeq)); - BEAST_EXPECT(ownerCount(env, alice_) == 2); + BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq)); + BEAST_EXPECT(ownerCount(env, alice) == 2); } // apply - by default, hybrid offer tries to cross with offers in the // domain book { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + auto const bobOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), 0, true)); - BEAST_EXPECT(ownerCount(env, bob_) == 3); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(ownerCount(env, bob) == 3); // hybrid offer auto crosses with domain offer - auto const aliceOfferSeq{env.seq(alice_)}; - env(offer(alice_, USD(10), XRP(10)), Domain(domainID), Txflags(tfHybrid)); + auto const aliceOfferSeq{env.seq(alice)}; + env(offer(alice, USD(10), XRP(10)), Domain(domainID), Txflags(tfHybrid)); env.close(); - BEAST_EXPECT(!offerExists(env, alice_, aliceOfferSeq)); - BEAST_EXPECT(!offerExists(env, bob_, bobOfferSeq)); - BEAST_EXPECT(ownerCount(env, alice_) == 2); + BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq)); + BEAST_EXPECT(ownerCount(env, alice) == 2); } // apply - hybrid offer does not automatically cross with open offers // because by default, it only tries to cross domain offers { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10))); + auto const bobOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10))); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), 0, false)); - BEAST_EXPECT(ownerCount(env, bob_) == 3); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, false)); + BEAST_EXPECT(ownerCount(env, bob) == 3); // hybrid offer auto crosses with domain offer - auto const aliceOfferSeq{env.seq(alice_)}; - env(offer(alice_, USD(10), XRP(10)), Domain(domainID), Txflags(tfHybrid)); + auto const aliceOfferSeq{env.seq(alice)}; + env(offer(alice, USD(10), XRP(10)), Domain(domainID), Txflags(tfHybrid)); env.close(); - BEAST_EXPECT(offerExists(env, alice_, aliceOfferSeq)); - BEAST_EXPECT(offerExists(env, bob_, bobOfferSeq)); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), 0, false)); - BEAST_EXPECT(checkOffer(env, alice_, aliceOfferSeq, USD(10), XRP(10), lsfHybrid, true)); - BEAST_EXPECT(ownerCount(env, alice_) == 3); + BEAST_EXPECT(offerExists(env, alice, aliceOfferSeq)); + BEAST_EXPECT(offerExists(env, bob, bobOfferSeq)); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, false)); + BEAST_EXPECT(checkOffer(env, alice, aliceOfferSeq, USD(10), XRP(10), lsfHybrid, true)); + BEAST_EXPECT(ownerCount(env, alice) == 3); } } @@ -1129,58 +1129,97 @@ class PermissionedDEX_test : public beast::unit_test::Suite { testcase("Hybrid invalid offer"); - // bob_ has a hybrid offer and then he is removed from domain. - // in this case, the hybrid offer will be considered as unfunded even in - // a regular payment + // bob has a hybrid offer and then he is removed from the domain. + // Domain payments must not consume the offer; regular open-book + // payments follow the fixCleanup3_3_0 behavior checked below. Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const hybridOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(50), USD(50)), Txflags(tfHybrid), Domain(domainID)); + auto const hybridOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(50), USD(50)), Txflags(tfHybrid), Domain(domainID)); env.close(); - // remove bob_ from domain - env(credentials::deleteCred(domainOwner, bob_, domainOwner, credType)); + // remove bob from domain + env(credentials::deleteCred(domainOwner, bob, domainOwner, credType)); env.close(); - // bob_'s hybrid offer is unfunded and can not be consumed in a domain + // bob's hybrid offer is unfunded and can not be consumed in a domain // payment - env(pay(alice_, carol_, USD(5)), + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID), Ter(tecPATH_PARTIAL)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, hybridOfferSeq, XRP(50), USD(50), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, hybridOfferSeq, XRP(50), USD(50), lsfHybrid, true)); - // bob_'s unfunded hybrid offer can't be consumed even with a regular - // payment - env(pay(alice_, carol_, USD(5)), Path(~USD), Sendmax(XRP(5)), Ter(tecPATH_PARTIAL)); - env.close(); - BEAST_EXPECT(checkOffer(env, bob_, hybridOfferSeq, XRP(50), USD(50), lsfHybrid, true)); + if (features[fixCleanup3_3_0]) + { + // Post-fixCleanup3_3_0: hybrid offer can still be consumed via a regular + // open-book payment even though the domain credential was revoked. + auto const carolBalBefore = env.balance(carol, USD); + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5))); + env.close(); + BEAST_EXPECT(env.balance(carol, USD) - carolBalBefore == USD(5)); + BEAST_EXPECT(checkOffer(env, bob, hybridOfferSeq, XRP(45), USD(45), lsfHybrid, true)); - // create a regular offer - auto const regularOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10))); - env.close(); - BEAST_EXPECT(offerExists(env, bob_, regularOfferSeq)); - BEAST_EXPECT(checkOffer(env, bob_, regularOfferSeq, XRP(10), USD(10))); + // create a regular offer alongside the hybrid one + auto const regularOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10))); + env.close(); + BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10))); - auto const sleHybridOffer = env.le(keylet::offer(bob_.id(), hybridOfferSeq)); - BEAST_EXPECT(sleHybridOffer); - auto const openDir = - sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory); - BEAST_EXPECT(checkDirectorySize(env, openDir, 2)); + auto const sleHybridOffer = env.le(keylet::offer(bob.id(), hybridOfferSeq)); + if (!BEAST_EXPECT(sleHybridOffer)) + return; + auto const openDir = + sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory); + // both offers are in the open book directory + BEAST_EXPECT(checkDirectorySize(env, openDir, 2)); - // this normal payment should consume the regular offer and remove the - // unfunded hybrid offer - env(pay(alice_, carol_, USD(5)), Path(~USD), Sendmax(XRP(5))); - env.close(); + // A regular payment crosses the hybrid offer first (FIFO, older + // offer), then stops; the regular offer is untouched. + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5))); + env.close(); - BEAST_EXPECT(!offerExists(env, bob_, hybridOfferSeq)); - BEAST_EXPECT(checkOffer(env, bob_, regularOfferSeq, XRP(5), USD(5))); - BEAST_EXPECT(checkDirectorySize(env, openDir, 1)); + BEAST_EXPECT(checkOffer(env, bob, hybridOfferSeq, XRP(40), USD(40), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10))); + BEAST_EXPECT(checkDirectorySize(env, openDir, 2)); + } + else + { + // Pre-fixCleanup3_3_0: the open-book traversal + // also runs the offerInDomain eviction check, so the hybrid offer + // is treated as unfunded and the regular payment fails. + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5)), Ter(tecPATH_PARTIAL)); + env.close(); + BEAST_EXPECT(checkOffer(env, bob, hybridOfferSeq, XRP(50), USD(50), lsfHybrid, true)); + + // create a regular offer + auto const regularOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10))); + env.close(); + BEAST_EXPECT(offerExists(env, bob, regularOfferSeq)); + BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10))); + + auto const sleHybridOffer = env.le(keylet::offer(bob.id(), hybridOfferSeq)); + if (!BEAST_EXPECT(sleHybridOffer)) + return; + auto const openDir = + sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory); + BEAST_EXPECT(checkDirectorySize(env, openDir, 2)); + + // This payment crosses the regular offer and permanently evicts the + // hybrid offer from the open book (since the payment succeeds, the + // sandbox, including the hybrid eviction, is committed). + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5))); + env.close(); + + BEAST_EXPECT(!offerExists(env, bob, hybridOfferSeq)); + BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(5), USD(5))); + BEAST_EXPECT(checkDirectorySize(env, openDir, 1)); + } } void @@ -1191,29 +1230,29 @@ class PermissionedDEX_test : public beast::unit_test::Suite // both non domain and domain payments can consume hybrid offer { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const hybridOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); + auto const hybridOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); env.close(); - env(pay(alice_, carol_, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID)); + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, hybridOfferSeq, XRP(5), USD(5), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, hybridOfferSeq, XRP(5), USD(5), lsfHybrid, true)); - // hybrid offer can't be consumed since bob_ is not in domain anymore - env(pay(alice_, carol_, USD(5)), Path(~USD), Sendmax(XRP(5))); + // hybrid offer can't be consumed since bob is not in domain anymore + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5))); env.close(); - BEAST_EXPECT(!offerExists(env, bob_, hybridOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, hybridOfferSeq)); } // someone from another domain can't cross hybrid if they specified // wrong domainID { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); // Fund accounts @@ -1235,8 +1274,8 @@ class PermissionedDEX_test : public beast::unit_test::Suite env(credentials::accept(devin, badDomainOwner, badCredType)); env.close(); - auto const hybridOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); + auto const hybridOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); env.close(); // other domains can't consume the offer @@ -1246,107 +1285,197 @@ class PermissionedDEX_test : public beast::unit_test::Suite Domain(badDomainID), Ter(tecPATH_DRY)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, hybridOfferSeq, XRP(10), USD(10), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, hybridOfferSeq, XRP(10), USD(10), lsfHybrid, true)); - env(pay(alice_, carol_, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID)); + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, hybridOfferSeq, XRP(5), USD(5), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, hybridOfferSeq, XRP(5), USD(5), lsfHybrid, true)); - // hybrid offer can't be consumed since bob_ is not in domain anymore - env(pay(alice_, carol_, USD(5)), Path(~USD), Sendmax(XRP(5))); + // hybrid offer can't be consumed since bob is not in domain anymore + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5))); env.close(); - BEAST_EXPECT(!offerExists(env, bob_, hybridOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, hybridOfferSeq)); } // test domain payment consuming two offers w/ hybrid offer { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const eur = gw_["EUR"]; - env.trust(eur(1000), alice_); + auto const eur = gw["EUR"]; + env.trust(eur(1000), alice); env.close(); - env.trust(eur(1000), bob_); + env.trust(eur(1000), bob); env.close(); - env.trust(eur(1000), carol_); + env.trust(eur(1000), carol); env.close(); - env(pay(gw_, bob_, eur(100))); + env(pay(gw, bob, eur(100))); env.close(); - auto const usdOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10)), Domain(domainID)); + auto const usdOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, usdOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true)); // payment fail because there isn't eur offer - env(pay(alice_, carol_, eur(5)), + env(pay(alice, carol, eur(5)), Path(~USD, ~eur), Sendmax(XRP(5)), Domain(domainID), Ter(tecPATH_PARTIAL)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, usdOfferSeq, XRP(10), USD(10), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true)); - // bob_ creates a hybrid eur offer - auto const eurOfferSeq{env.seq(bob_)}; - env(offer(bob_, USD(10), eur(10)), Domain(domainID), Txflags(tfHybrid)); + // bob creates a hybrid eur offer + auto const eurOfferSeq{env.seq(bob)}; + env(offer(bob, USD(10), eur(10)), Domain(domainID), Txflags(tfHybrid)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, eurOfferSeq, USD(10), eur(10), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, eurOfferSeq, USD(10), eur(10), lsfHybrid, true)); - // alice_ successfully consume two domain offers: xrp/usd and usd/eur - env(pay(alice_, carol_, eur(5)), Path(~USD, ~eur), Sendmax(XRP(5)), Domain(domainID)); + // alice successfully consume two domain offers: xrp/usd and usd/eur + env(pay(alice, carol, eur(5)), Path(~USD, ~eur), Sendmax(XRP(5)), Domain(domainID)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, usdOfferSeq, XRP(5), USD(5), 0, true)); - BEAST_EXPECT(checkOffer(env, bob_, eurOfferSeq, USD(5), eur(5), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(5), USD(5), 0, true)); + BEAST_EXPECT(checkOffer(env, bob, eurOfferSeq, USD(5), eur(5), lsfHybrid, true)); } // test regular payment using a regular offer and a hybrid offer { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const eur = gw_["EUR"]; - env.trust(eur(1000), alice_); + auto const eur = gw["EUR"]; + env.trust(eur(1000), alice); env.close(); - env.trust(eur(1000), bob_); + env.trust(eur(1000), bob); env.close(); - env.trust(eur(1000), carol_); + env.trust(eur(1000), carol); env.close(); - env(pay(gw_, bob_, eur(100))); + env(pay(gw, bob, eur(100))); env.close(); - // bob_ creates a regular usd offer - auto const usdOfferSeq{env.seq(bob_)}; - env(offer(bob_, XRP(10), USD(10))); + // bob creates a regular usd offer + auto const usdOfferSeq{env.seq(bob)}; + env(offer(bob, XRP(10), USD(10))); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, usdOfferSeq, XRP(10), USD(10), 0, false)); + BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, false)); - // bob_ creates a hybrid eur offer - auto const eurOfferSeq{env.seq(bob_)}; - env(offer(bob_, USD(10), eur(10)), Domain(domainID), Txflags(tfHybrid)); + // bob creates a hybrid eur offer + auto const eurOfferSeq{env.seq(bob)}; + env(offer(bob, USD(10), eur(10)), Domain(domainID), Txflags(tfHybrid)); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, eurOfferSeq, USD(10), eur(10), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, eurOfferSeq, USD(10), eur(10), lsfHybrid, true)); - // alice_ successfully consume two offers: xrp/usd and usd/eur - env(pay(alice_, carol_, eur(5)), Path(~USD, ~eur), Sendmax(XRP(5))); + // alice successfully consume two offers: xrp/usd and usd/eur + env(pay(alice, carol, eur(5)), Path(~USD, ~eur), Sendmax(XRP(5))); env.close(); - BEAST_EXPECT(checkOffer(env, bob_, usdOfferSeq, XRP(5), USD(5), 0, false)); - BEAST_EXPECT(checkOffer(env, bob_, eurOfferSeq, USD(5), eur(5), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, usdOfferSeq, XRP(5), USD(5), 0, false)); + BEAST_EXPECT(checkOffer(env, bob, eurOfferSeq, USD(5), eur(5), lsfHybrid, true)); } } + // Test that a hybrid offer remains crossable in the open book after the + // owner's domain credential expires. A domain payment after expiry should + // fail (domain book evicts the offer in its sandbox), but the open book + // remains usable. + void + testHybridOpenBookAfterCredentialExpiry(FeatureBitset features) + { + testcase("Hybrid open book after credential expiry"); + + Env env(*this, features); + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = + PermissionedDEX(env); + + Account const devin("devin"); + env.fund(XRP(100000), devin); + env.close(); + env.trust(USD(1000), devin); + env.close(); + env(pay(gw, devin, USD(100))); + env.close(); + + // Give devin a credential that expires far enough in the future to + // survive the setup env.close() calls. + auto jv = credentials::create(devin, domainOwner, credType); + uint32_t const t = env.current()->header().parentCloseTime.time_since_epoch().count(); + jv[sfExpiration.jsonName] = t + 100; + env(jv); + env.close(); + env(credentials::accept(devin, domainOwner, credType)); + env.close(); + + // Devin creates a hybrid offer: sell USD(10) for XRP(10). + // The offer is placed in both the domain book and the open book. + auto const hybridOfferSeq{env.seq(devin)}; + env(offer(devin, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); + env.close(); + + BEAST_EXPECT(checkOffer(env, devin, hybridOfferSeq, XRP(10), USD(10), lsfHybrid, true)); + + // A non-domain open-book payment partially crosses the offer while + // devin's credential is still valid. + auto carolBalance = env.balance(carol, USD); + env(pay(alice, carol, USD(5)), Path(~USD), Sendmax(XRP(5))); + env.close(); + BEAST_EXPECT(env.balance(carol, USD) - carolBalance == USD(5)); + BEAST_EXPECT(checkOffer(env, devin, hybridOfferSeq, XRP(5), USD(5), lsfHybrid, true)); + + // Advance time so that devin's credential expires. + env.close(std::chrono::seconds(100)); + + // Confirm devin can no longer create domain offers. + env(offer(devin, XRP(1), USD(1)), Domain(domainID), Ter(tecNO_PERMISSION)); + env.close(); + + // The hybrid offer must still exist in the open book after expiry. + BEAST_EXPECT(offerExists(env, devin, hybridOfferSeq)); + + // A non-domain open-book payment must cross (not evict) the + // remaining portion of devin's hybrid offer. + carolBalance = env.balance(carol, USD); + env(pay(alice, carol, USD(2)), Path(~USD), Sendmax(XRP(2))); + env.close(); + + // Carol received USD; the offer was crossed, not evicted. + BEAST_EXPECT(env.balance(carol, USD) - carolBalance == USD(2)); + // Offer still exists with 3 USD / 3 XRP remaining. + BEAST_EXPECT(checkOffer(env, devin, hybridOfferSeq, XRP(3), USD(3), lsfHybrid, true)); + + // A domain payment now fails because the domain book evicts devin's + // offer (his credential has expired). The eviction is rolled back with + // the failed sandbox, so the offer is NOT permanently removed. + env(pay(alice, carol, USD(1)), + Path(~USD), + Sendmax(XRP(1)), + Domain(domainID), + Ter(tecPATH_PARTIAL)); + env.close(); + + // Offer still intact in the open book; domain payment did not + // permanently delete it. + BEAST_EXPECT(checkOffer(env, devin, hybridOfferSeq, XRP(3), USD(3), lsfHybrid, true)); + + // The open book can still fully consume the remaining portion. + carolBalance = env.balance(carol, USD); + env(pay(alice, carol, USD(3)), Path(~USD), Sendmax(XRP(3))); + env.close(); + BEAST_EXPECT(env.balance(carol, USD) - carolBalance == USD(3)); + BEAST_EXPECT(!offerExists(env, devin, hybridOfferSeq)); + } + void testHybridOfferDirectories(FeatureBitset features) { Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); std::vector offerSeqs; @@ -1362,12 +1491,12 @@ class PermissionedDEX_test : public beast::unit_test::Suite for (size_t i = 1; i <= dirCnt; i++) { - auto const bobOfferSeq{env.seq(bob_)}; + auto const bobOfferSeq{env.seq(bob)}; offerSeqs.emplace_back(bobOfferSeq); - env(offer(bob_, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); + env(offer(bob, XRP(10), USD(10)), Txflags(tfHybrid), Domain(domainID)); env.close(); - auto const sleOffer = env.le(keylet::offer(bob_.id(), bobOfferSeq)); + auto const sleOffer = env.le(keylet::offer(bob.id(), bobOfferSeq)); BEAST_EXPECT(sleOffer); BEAST_EXPECT(sleOffer->getFieldH256(sfBookDirectory) == domainDir); BEAST_EXPECT(sleOffer->getFieldArray(sfAdditionalBooks).size() == 1); @@ -1375,17 +1504,17 @@ class PermissionedDEX_test : public beast::unit_test::Suite sleOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(sfBookDirectory) == openDir); - BEAST_EXPECT(checkOffer(env, bob_, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true)); + BEAST_EXPECT(checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true)); BEAST_EXPECT(checkDirectorySize(env, domainDir, i)); BEAST_EXPECT(checkDirectorySize(env, openDir, i)); } for (auto const offerSeq : offerSeqs) { - env(offerCancel(bob_, offerSeq)); + env(offerCancel(bob, offerSeq)); env.close(); dirCnt--; - BEAST_EXPECT(!offerExists(env, bob_, offerSeq)); + BEAST_EXPECT(!offerExists(env, bob, offerSeq)); BEAST_EXPECT(checkDirectorySize(env, domainDir, dirCnt)); BEAST_EXPECT(checkDirectorySize(env, openDir, dirCnt)); } @@ -1397,34 +1526,34 @@ class PermissionedDEX_test : public beast::unit_test::Suite testcase("Auto bridge"); Env env(*this, features); - auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = + auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - auto const eur = gw_["EUR"]; + auto const eur = gw["EUR"]; - for (auto const& account : {alice_, bob_, carol_}) + for (auto const& account : {alice, bob, carol}) { env(trust(account, eur(10000))); env.close(); } - env(pay(gw_, carol_, eur(1))); + env(pay(gw, carol, eur(1))); env.close(); - auto const aliceOfferSeq{env.seq(alice_)}; - auto const bobOfferSeq{env.seq(bob_)}; - env(offer(alice_, XRP(100), USD(1)), Domain(domainID)); - env(offer(bob_, eur(1), XRP(100)), Domain(domainID)); + auto const aliceOfferSeq{env.seq(alice)}; + auto const bobOfferSeq{env.seq(bob)}; + env(offer(alice, XRP(100), USD(1)), Domain(domainID)); + env(offer(bob, eur(1), XRP(100)), Domain(domainID)); env.close(); - // carol_'s offer should cross bob_ and alice_'s offers due to auto + // carol's offer should cross bob and alice's offers due to auto // bridging - auto const carolOfferSeq{env.seq(carol_)}; - env(offer(carol_, USD(1), eur(1)), Domain(domainID)); + auto const carolOfferSeq{env.seq(carol)}; + env(offer(carol, USD(1), eur(1)), Domain(domainID)); env.close(); - BEAST_EXPECT(!offerExists(env, bob_, aliceOfferSeq)); - BEAST_EXPECT(!offerExists(env, bob_, bobOfferSeq)); - BEAST_EXPECT(!offerExists(env, bob_, carolOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, aliceOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq)); + BEAST_EXPECT(!offerExists(env, bob, carolOfferSeq)); } void @@ -1819,7 +1948,9 @@ public: // Test hybrid offers testHybridOfferCreate(all); testHybridBookStep(all); + testHybridInvalidOffer(all - fixCleanup3_3_0); testHybridInvalidOffer(all); + testHybridOpenBookAfterCredentialExpiry(all); testHybridOfferDirectories(all); testHybridMalformedOffer(all); testHybridMalformedOffer(all - fixCleanup3_1_3); From cee157485e1e08181a37afc579bdd6f8fabf263e Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 11 Jun 2026 13:59:22 +0100 Subject: [PATCH 87/87] ci: Run sanitizers on release builds too (#7527) --- .github/scripts/strategy-matrix/linux.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index e6c807ac95..4f45216cda 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -10,7 +10,7 @@ { "compiler": ["gcc", "clang"], - "build_type": ["Debug"], + "build_type": ["Debug", "Release"], "arch": ["amd64"], "sanitizers": ["address", "undefinedbehavior"] },